Русский
Русский
English
Статистика
Реклама

Шифрование

Стражи публичных облаков как мы внедряли анклавы Intel SGX для защиты чувствительных данных

14.01.2021 16:09:15 | Автор: admin
Как развеять предубеждения потенциальных пользователей относительно безопасности публичных облаков? На помощь приходит технология Intel Software Guard Extensions (Intel SGX). Рассказываем, как мы внедрили её в своём облаке и какие преимущества от нашего решения получила компания Aggregion.





Кратко об Intel SGX и его роли в облаке


Intel Software Guard Extensions (Intel SGX) набор инструкций ЦП, с помощью которых в адресном пространстве приложения создаются частные защищённые области (анклавы), где размещается код уровня пользователя. Технология обеспечивает конфиденциальность и целостность чувствительных данных. Путём изоляции в анклаве они получают дополнительную защиту как от несанкционированного внешнего доступа, в том числе со стороны провайдера облачных услуг, так и от внутренних угроз, включая атаки со стороны программного обеспечения привилегированного уровня.

Принципы работы. Для хранения кода и данных анклавов технология Intel SGX выделяет область памяти Processor Reserved Memory (PRM). ЦП защищает её от всех внешних обращений, в том числе от доступа со стороны ядра и гипервизора. В PRM содержится Enclave Page Cache (EPC), состоящий из блоков страниц объёмом 4 КиБ, при этом каждая страница должна принадлежать только одному анклаву, а их состояние фиксируется в Enclave Page Cache Metadata (EPCM) и контролируется ЦП.

Безопасность EPC обеспечивается за счёт Memory Encryption Engine (MEE), который генерирует ключи шифрования, хранящиеся в ЦП. Предполагается, что страницы могут быть расшифрованы только внутри физического ядра процессора.

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

Наш подход к внедрению Intel SGX


Чтобы в публичном облаке G-Core Labs появилась возможность выделять виртуальные машины с анклавами Intel SGX, нам пришлось пройти путь от компиляции патченного ядра KVM и QEMU до написания Python-скриптов в сервисах OpenStack Nova. Вычислительные узлы, которые планировалось использовать для выделения виртуальных машин повышенной безопасности, мы решили определить в отдельный агрегатор тип вычислительных ресурсов, требующий дополнительной настройки. На таких узлах было необходимо:

  • Включить поддержку Intel SGX на уровне BIOS.
  • Поставить патченные QEMU/KVM.

Изначально у нас не было понимания, как это должно работать и что в итоге мы должны прикрутить, чтобы получить ВМ нужной конфигурации. Разобраться с этим вопросом нам частично помогло руководство Intel для разработчиков. С его помощью мы узнали, как подготовить вычислительный узел для работы с SGX и какими дополнительными параметрами должен обладать конфигурационный XML-файл виртуальной машины. Здесь же мы нашли исчерпывающую информацию, как с помощью виртуализации KVM сделать гостевую машину с использованием Intel SGX. Чтобы убедиться, что мы смогли обеспечить поддержку данной технологии, мы использовали два способа:

  • Проверили секцию /dev/sgx/virt_epc в файле /proc/$PID/smaps:

    [root@compute-sgx ~]# grep -A22 epc /proc/$PID/smaps7f797affe000-7f797b7fe000 rw-s 00000000 00:97 57466526/dev/sgx/virt_epcSize:               8192 kBKernelPageSize:        4 kBMMUPageSize:           4 kBRss:                   0 kBPss:                   0 kBShared_Clean:          0 kBShared_Dirty:          0 kBPrivate_Clean:         0 kBPrivate_Dirty:         0 kBReferenced:            0 kBAnonymous:             0 kBLazyFree:              0 kBAnonHugePages:         0 kBShmemPmdMapped:        0 kBFilePmdMapped:         0 kBShared_Hugetlb:        0 kBPrivate_Hugetlb:       0 kBSwap:                  0 kBSwapPss:               0 kBLocked:                0 kBTHPeligible:0VmFlags: rd wr sh mr mw me ms pf io dc dd hg
    
  • И воспользовались данным shell-скриптом, предварительно поставив SGX-драйвер (все действия осуществлялись внутри ВМ):

    [root@sgx-vm ~]# cat check_sgx.sh#!/bin/bashMETRICS="sgx_nr_total_epc_pages \    sgx_nr_free_pages \    sgx_nr_low_pages \    sgx_nr_high_pages \    sgx_nr_marked_old \    sgx_nr_evicted \    sgx_nr_alloc_pages \    "MODPATH="/sys/module/isgx/parameters/"for metric in $METRICS ; do    echo "$metric= `cat $MODPATH/$metric`"done[root@sgx-vm ~]# curl -fsSL https://raw.githubusercontent.com/scontain/SH/master/install_sgx_driver.sh | bash -s - install -p metrics -p page0[root@sgx-vm ~]# ./check_sgx.shsgx_nr_total_epc_pages= 2048sgx_nr_free_pages= 2048sgx_nr_low_pages= 32sgx_nr_high_pages= 64sgx_nr_marked_old= 0sgx_nr_evicted= 0sgx_nr_alloc_pages= 0
    

    Стоит учитывать, что, если одна страница занимает 4 КиБ, то для 2048 страниц требуется 8 МиБ (2048 x 4 = 8192).

Трудности разработки и их преодоление


Отсутствие какой-либо технической документации по интеграции Intel SGX в OpenStack было нашей основной трудностью на момент внедрения. Поиск привел нас к статье проекта SecureCloud, где был представлен способ управления виртуальными машинами с анклавами SGX.

Найденная информация помогла понять, над чем именно нам предстоит работать. В результате мы сформировали следующие задачи:

  1. Добиться от сервиса OpenStack Nova генерации XML-файла с дополнительными параметрами для виртуальных машин с поддержкой Intel SGX.
  2. Написать фильтр планировщика OpenStack Nova с целью определения доступной памяти для анклавов на вычислительных узлах и осуществления некоторых других проверок.

Их выполнения было достаточно для интеграции Intel SGX в наше публичное облако.

Кроме того, мы дописали сбор статистики с учетом EPC:

# openstack usage showUsage from 2020-11-04 to 2020-12-03 on project a968da75bcab4943a7beb4009b8ccb4a:+---------------+--------------+| Field         | Value        |+---------------+--------------+| CPU Hours     | 47157.6      || Disk GB-Hours | 251328.19    || EPC MB-Hours  | 26880.02     || RAM MB-Hours  | 117222622.62 || Servers       | 23           |+---------------+--------------+


Безопасная среда для запуска контейнеризированных приложений



Научившись выделять виртуальные машины с поддержкой Intel SGX, мы использовали платформу SCONE компании Scontain, чтобы обеспечить возможность безопасного запуска контейнеризированных приложений в случае угроз со стороны привилегированного программного обеспечения. При использовании данного решения для прозрачной защиты файловых систем в средах Docker, Kubernetes и Rancher достаточно наличия процессора Intel с поддержкой SGX и драйвера Linux SGX.

Запуск каждого из контейнеров возможен лишь при наличии файла конфигурации, создаваемого клиентским расширением платформы SCONE. В нём содержатся ключи шифрования, аргументы приложения и переменные среды. Файлы, сетевой трафик и стандартные потоки ввода/вывода (stdin/stdout) прозрачно зашифрованы и недоступны даже для пользователей с правами root.
Платформа SCONE оснащена встроенной службой аттестации и конфигурации, проверяющей приложения на соответствие принятой политике безопасности. Она генерирует приватные ключи и сертификаты, которые должны быть доступны только в пределах анклава. Конфиденциальность и целостность данных в процессе их передачи обеспечиваются криптографическим протоколом TLS.

С помощью драйвера SGX для каждого анклава в виртуальном адресном пространстве резервируется до 64 ГБ памяти. Платформа SCONE поддерживает языки программирования C/C++/C#/Rust/Go/Python/Java. За счёт специального компилятора исходный код автоматически (без необходимости дополнительных модификаций) подготавливается к использованию совместно с Intel SGX.

Кейс Aggregion


Завершив все необходимые работы по интеграции Intel SGX, мы подключили к нашему публичному облаку платформу управления распределёнными данными компании Aggregion.

Она предназначена для реализации совместных маркетинговых проектов представителями различных отраслей финансовых и страховых услуг, государственного управления, телекоммуникаций, ритейла. Партнёры анализируют поведение потребителей, развивают таргетированное продвижение товаров и услуг, разрабатывают востребованные программы лояльности, обмениваясь и обрабатывая обезличенные массивы данных на платформе Aggregion. Поскольку утечка конфиденциальной информации крайне не желательна и грозит серьёзными репутационными рисками, компания уделяет особое внимание вопросам безопасности.

Софт Aggregion целиком ставится в контур поставщика данных, что подразумевает наличие в его распоряжении инфраструктуры с поддержкой Intel SGX. Теперь клиенты компании могут рассматривать подключение к нашему публичному облаку в качестве альтернативы аренде или покупке физических серверов.

Принципы безопасной работы на платформе Aggregion. В контуре каждого поставщика чувствительные данные изолируются в анклавы Intel SGX, которые фактически представляют собой чёрные ящики: что происходит внутри, недоступно никому, в том числе и провайдеру облачной инфраструктуры. Проверка первоначального состояния анклава и возможности его использования для хранения конфиденциальной информации осуществляется за счёт удалённой аттестации, когда MrEnclave определяет хеш-значение.

Потенциальная польза для клиентов. Комбинирование баз данных нескольких поставщиков позволяет повысить эффективность совместных рекламных кампаний. При выделении целевой аудитории по заданным параметрам мэтчинг (сопоставление) сегментов выполняется непосредственно внутри контейнеров с поддержкой анклавов Intel SGX. За пределы выводится только конечный результат: например, численность пользователей, соответствующих выбранным атрибутам. Аналогичным образом оценивается эффективность проведенных кампаний: в анклавы выгружаются данные о рекламных показах и совершённых продажах для вычисления прироста покупок целевой группы относительно контрольной, который и выдаётся наружу для дальнейшего использования.



Выводы


Мы понимаем, что Intel SGX не является панацеей по защите данных и можно найти ряд статей, порицающих эту технологию, в том числе и на Хабре. Периодически появляются сообщения об атаках, способных извлечь конфиденциальные данные из анклавов: так, в 2018 году бреши в SGX пробили Meltdown и Spectre, в 2020 году SGAxe и CrossTalk. В свою очередь компания Intel устраняет выявленные уязвимости с помощью обновлений микрокода процессоров.

Почему же мы всё-таки решили внедрить данную технологию? Мы видим в применении Intel SGX возможность сократить потенциальную область кибератак за счёт создания дополнительного контура защиты облачной инфраструктуры G-Core Labs наряду с уже задействованными технологиями информационной безопасности и тем самым повысить доверие наших пользователей к хранению и обработке конфиденциальных данных. Надеемся, что в будущем нам ещё предстоит поделиться с вами успешными клиентскими кейсами, хотя и не берёмся утверждать, что наши статьи не будут основаны на историях обнаружения и устранения новых уязвимостей. А пока предлагаем вам поделиться своими методами по защите чувствительных данных в комментариях.
Подробнее..

Привет из прошлого кто, что и зачем пишет в журнале учета СКЗИ

11.02.2021 10:09:47 | Автор: admin

Скорее всего, вы знаете, что учет СКЗИ регламентирован Инструкцией об организации и обеспечении безопасности хранения, обработки и передачи по каналам связи с использованием средств криптографической защиты информации с ограниченным доступом, не содержащих сведений, составляющих государственную тайну. Этот документ невиданной силы утвержден приказом Федерального агентства правительственной связи и информации при президенте Российской Федерации (ФАПСИ) от 13.06.2001 152.

Тогда, 20 лет назад, сертифицированные СКЗИ применялись крайне редко, а большинство организаций не имели такой разрозненной и распределенной по всей стране ИТ-инфраструктуры. В итоге инструкция, например, до сих пор не предусматривает возможность удаленной передачи ключей шифрования, а журнал учета требуется хранить в отдельном помещении. Вести учет в электронном виде можно, но только с применением квалифицированной ЭП (учет СКЗИ в электронном виде это тема для отдельной статьи, и об этом мы поговорим следующий раз), либо сопровождать каждое действие соответствующими актами.

Кстати, само ФАПСИ было расформировано в 2003 году. Его функции были распределены между ФСО, ФСБ, СВР и Службой специальной связи и информации при ФСО. Но написанный агентством документ не утратил силы.

Кто и как ведет учет

Если организация является обладателем конфиденциальной информации, то ей, скорее всего, требуется обеспечить безопасную передачу, обработку и хранение этой информации с помощью СКЗИ. К слову, к последним инструкция относит как сами программные или аппаратно-программные средства, так и информацию, необходимую для их работы, ключи, техническую документацию.

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

В первом случае организация должна издать приказ о создании ОКЗ, определить его структуру и обязанности сотрудников. Например:

  • начальник отдела занимается организацией и совершенствованием системы управления работой своих сотрудников;

  • администратор безопасности обеспечивает сохранность информации, обрабатываемой, передаваемой и хранимой при помощи средств вычислительной техники.

Все работники, которые занимаются установкой и настройкой СКЗИ и в принципе имеют к ним доступ, должны быть внесены в приказ и ознакомлены с ним. Для каждой должности нужно разработать должностную инструкцию и ознакомить пользователей с порядком применения СКЗИ.

В итоге перечень необходимых документов состоит из:

  • приказа о создании ОКЗ;

  • должностных инструкций;

  • утвержденных форм журналов учета;

  • шаблонов заявлений, актов;

  • инструкции для пользователей по работе с СКЗИ.

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

Но если орган криптографической защиты информации это структурное подразделение организации, на его плечи ложится ведение обоих журналов. Дело в том, что в этом случае организация не только является обладателем конфиденциальной информации, но и выполняет часть функций ОКЗ. Например, крупные холдинги, как правило, выделяют в своем составе ИТ-компанию, которая в том числе отвечает за информационную безопасность с использованием СКЗИ. Она ведет все журналы и сопутствующую документацию и является для своего холдинга поставщиком услуг.

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

Вы еще тут? Надеемся, не запутались!

Журналы учета хранятся в течение 5 лет. Сами СКЗИ и документация к ним должны находиться за семью замками в специальном помещении, а доступ туда могут иметь только сотрудники ОКЗ.

Операции с СКЗИ: взятие на учет

Рассмотрим порядок учета на конкретном примере (данные в таблицах ниже вымышленные все совпадения случайны). Организация N обладатель конфиденциальной информации хочет использовать СКЗИ компании КриптоПро. При этом организация N не готова создавать у себя ОКЗ и обращается к сервис-провайдеру, который будет предоставлять ей соответствующие услуги. Итак, для начала вендор ПАК должен предоставить организации N исходные данные для учета. Выглядит это так:

Предмет

Данные

с/н лицензии СКЗИ

Сопроводительное письмо

т/н 44313 от 22.02.2020

-

Формуляр СКЗИ КриптоПро CSP версии 4.2

ЖТЯИ.00002-02 30 10

-

Диск CD-ROM с дистрибутивом СКЗИ

Инв. 5421

34DR-4231-4123-0003-1200

HR9J-EEWR-45W3-VL4Q-842Q

4454-NG96-8421-6401-WQRG

В 16 графы журнала поэкземплярного учета должна попасть информация о:

диске с дистрибутивом;

формуляре;

серийном номере лицензии на СКЗИ.

После заполнения всех этих данных СКЗИ выдаются пользователям, то есть тем работникам в организации, для которых они закупались. Это может быть как сотрудник бухгалтерии, который применяет ЭП для подписания и отправки документов, так и другой ответственный специалист, взявший на себя обязательства по сохранности СЗКИ.

На этом этапе заполняются 7 и 8 графы журнала (кому и когда выдается СКЗИ с обязательной росписью пользователя). Если возможности расписаться в журнале нет, то можно заполнить акт передачи, где в свободной форме указывается, кто (администратор безопасности) и кому (пользователю) передает СКЗИ. При этом обе стороны расписываются, а номер акта вносится в 8 графу (Дата и номер сопроводительного письма).

В 9 графу записывается имя сотрудника, производившего установку СКЗИ. Чаще всего это делает специалист технической поддержки, который также является администратором безопасности. Но это может быть и пользователь, если он обладает соответствующими навыками и правами доступа в сеть. В 11 графе указывается серийный номер материнской планы или номер опечатывающей пломбы системного блока.

Если сотрудник, который производил установку, уволился, то СКЗИ нужно изъять и составить акт, в котором указывается предмет и способ изъятия (например, удаление ключевой информации с носителя). Все это фиксируются в 12, 13, 14 графах.

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

Ниже пример журнала, заполненного организацией обладателем конфиденциальной информации. ООО Компания это сервис-провайдер, который выполняет функции органа криптографической защиты для организации.

Заглянуть в журнал учета
Журнал поэкземплярного учета СКЗИ для обладателя конфиденциальной информацииЖурнал поэкземплярного учета СКЗИ для обладателя конфиденциальной информации

Журнал учета СКЗИ для органа криптографической защиты во многих пунктах перекликается с аналогичным документом для организации и заполняется по такому же принципу, поэтому не будем подробно останавливаться на его разборе. В примере ниже ООО Организация это обладатель конфиденциальной информации, который воспользовался услугами сервис-провайдера.

Заглянуть в журнал еще раз
Журнал поэкземплярного учета СКЗИ для органа криптографической защитыЖурнал поэкземплярного учета СКЗИ для органа криптографической защиты

Что в итоге?

Сложно не согласиться с тем, что все эти требования уже давно морально устарели и Инструкция нуждается в актуальных корректировках, но пока мы вынуждены выполнять требования ее текущей редакции. Обратите внимание, что для ведения журнала поэкземплярного учета СКЗИ в электронном виде требуется подписывать документы только квалифицированной ЭП либо сопровождать каждое действие соответствующими актами. Если же речь идет о физическом документе, то все данные и подписи должны быть внесены в него всеми ответственными лично.

Безусловно, учет СКЗИ это только один из множества обязательных к исполнению процессов, описанных в документе. В будущем мы постараемся подробно описать процесс опломбирования СКЗИ, технологию их уничтожения и многое другое.

Надеемся, эта памятка была для вас полезной.

Автор: Никита Никиточкин, администратор реестра СКЗИ Solar JSOC Ростелеком-Солар

Подробнее..

Перевод Примеры грамотного применения SSH-шаблонов

17.02.2021 20:19:33 | Автор: admin


SSH-сертификаты очень мощный инструмент. Первоначально в удостоверяющем центре step-ca мы реализовали только минимальный набор функций для аутентификации по сертификатам пользователя и хоста. Затем добавили шаблоны сертификатов X.509, а ещё в августе прошлого года и SSH-шаблоны, в версии 0.15.2. Наконец, мы задокументировали эту функцию и готовы о ней рассказать.

Шаблоны для сертификатов SSH действуют аналогично шаблонам X.509: это JSON-файлы, написанные в Go text/template. Они применяются для настройки SSH-сертификатов, которые выдаёт step-ca. Давайте посмотрим, что представляют собой эти шаблоны и как их можно использовать.

По умолчанию шаблон пользовательского SSH-сертификата выглядит так:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {{ toJson .Extensions }},"criticalOptions": {{ toJson .CriticalOptions }}}

А вот SSH-сертификат, выданный по этому шаблону:

$ step ssh inspect id_ct-cert.pubid_ct-cert.pub:        Type: ecdsa-sha2-nistp256-cert-v01@openssh.com user certificate        Public key: ECDSA-CERT SHA256:iczSh1XiBBE36yfJcDidgp6fqY3qWx1RtEwFfAN9jDs        Signing CA: ECDSA SHA256:MKwRQ/SDKk/pCJbbCk5bfhZACjSjv7uZXLyc5n4Wx6k        Key ID: "carl@smallstep.com"        Serial: 2831574724231262409        Valid: from 2020-11-17T16:48:11 to 2020-11-18T08:49:11        Principals:                carl                carl@smallstep.com        Critical Options: (none)        Extensions:                permit-X11-forwarding                permit-agent-forwarding                permit-port-forwarding                permit-pty                permit-user-rc

Он позволяет юзеру carl (или carl@smallstep.com) пройти аутентификацию на любом SSH-хосте, который доверяет моему SSH CA. Сертификат включает в себя некоторые основные расширения:

  • permit-x11-forwarding: Разрешает переадресацию X11 (с помощью ssh -X) для запуска удалённых программ X11 на локальном дисплее.
  • permit-agent-forwarding: Разрешает переадресацию агента (с помощью ssh -A) для пересылки ключей из локального агента SSH на удалённый хост (подробнее про агента SSH см. здесь).
  • permit-port-forwarding: Разрешает переадресацию портов (туннель) с локального на удалённый порт (ssh -L) или с удалённого на локальный (ssh -R).
  • permit-pty: Очень важное расширение. Если хотите открыть интерактивную сессию в консоли, хост должен выделить вам pty (псевдо-tty). В противном случае не предусмотрено никакой интерактивности. Например, для проверки SSH-аутентификации на GitHub можно запустить ssh -T git@github.com (-T отключает запрос на pty).
  • permit-user-rc: Запуск личного RC-файла после подключения (находится в ~/.ssh/rc на удалённом хосте).

Сертификаты пользователя и хоста поддерживают расширения и критические параметры, но OpenSSH не определяет никаких встроенных расширений или критических параметров для сертификатов хоста. Таким образом, всё самое интересное происходит именно с сертификатами пользователя, поэтому в данной статье рассмотрим только их.

Примеры шаблонов сертификатов


Внесём несколько изменений в дефолтный шаблон.

Запрещаем переадресацию агента и портов

Если пользователи подключаются к внутренним хостам через хост-бастион, то хорошо бы запретить перенаправление портов по соображениям безопасности. Вы же не хотите, чтобы юзеры перенаправили трафик с продакшн-сервера MySQL на свой localhost. Так же и переадресация агента сопряжена с риском для безопасности. Вот шаблон, который просто удаляет эти два расширения из сертификатов SSH:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {           "permit-x11-forwarding": "",           "permit-pty": "",           "permit-user-rc": ""  },"criticalOptions": {{ toJson .CriticalOptions }}}

Встраиваем директиву force-command

ForceCommand это серверная директива конфигурации SSHD, которая вместо интерактивного терминала запускает на хосте альтернативную команду. Но с тем же эффектом можно встроить force-command прямо в сертификат в раздел Critical Options:. Может пригодиться для служебных аккаунтов, которым необходимо выполнить только одну команду, например, запустить задание в удалённой системе.

Ограничиваем соединения по адресам

Чтобы ограничить область использования сертификата, в него можно встроить список разрешённых IP-адресов (блоков CIDR).

Вот шаблон сертификата, который использует и source-address, и force-command.

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {{ toJson .Extensions }},"criticalOptions": {"force-command": "echo \"Hello World\"","source-address": "10.20.30.0/24,1.1.1.1/32"}}

Это нормальный пример, но здесь список IP жёстко зафиксирован и не изменяется. А нам обычно хочется использовать разные списки адресов для разных пользователей. Давайте попробуем

Вставляем разные значения для разных юзеров

Очевидно, пользователям нельзя давать право редактировать диапазон адресов. Следовательно, динамические значения должны поступать из надёжного источника.

Для этого в step-ca можно через поставщика OpenID Connect (OIDC) настроить провайдера OAuth на добавление к токену нестандартных требований (custom claim), содержащих блоки адресов CIRD, которые мы хотим добавить в сертификат этого пользователя.

Поставщик OIDC представляет собой идеальный способ выдачи SSH-сертификатов в step-ca. В статье DIY Single Sign-On for SSH я рассказывал, как настроить SSH CA, чтобы он выдавал краткосрочные SSH-сертификаты по ID-токенам от доверенного провайдера OAuth. Если step-ca настроен как доверенный клиент OAuth, он будет считывать поле email из токена ID и извлекать оттуда список участников SSH-сертификата (например, по полю carl@smallstep.com сгенерируются сертификаты для carl и carl@smallstep.com).

Но OIDC позволяет через шаблоны считать из токенов ID и другие поля. Вот где начинается настоящая магия. Таким образом, добавляем в каталог пользователей на стороне провайдера идентификации отдельное поле source_address и отражаем его в нашем ID-токене. Затем через шаблон SSH можно ввести значение из токена в сертификат. Вот шаблон:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {{ toJson .Extensions }},{{ if .Token.source_address }}"criticalOptions": {"source-address": "{{ .Token.source_address }}"}{{ else }}"criticalOptions": {{ toJson .CriticalOptions }}{{ end }}}

Аутентификация на GitHub по сертификату

Рассмотрим ещё один пример custom claim. С помощью GitHub Enterprise Cloud или GitHub Enterprise Server можно настроить GitHub на использование SSH-сертификатов. В частности, GitHub будет доверять вашему центру сертификации SSH. Но чтобы всё заработало, нужно создать для каждого пользователя отдельный SSH-сертификат с расширением login@github.com, в котором прописывается имя пользователя на GitHub. С помощью этого расширения сертификат аутентифицирует пользователя на GitHub Enterprise. И это здорово: один и тот же сертификат позволяет и подключиться к вашему серверу по SSH, и запушить код на GitHub.

Вот шаблон сертификата с поддержкой индивидуального расширения GitHub:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"criticalOptions": {{ toJson .CriticalOptions }},{{ if .Token.ghu }}"extensions": {  "login@github.com": {{ toJson .Token.ghu }}}{{ else }}"extensions": {{ toJson .Extensions }}{{ end }}}

Для использования шаблона нужно добавить к токенам идентификации OIDC индивидуальное требование ghu (GitHub Username). Давайте подробно рассмотрим, как создать этот custom claim с помощью вашего провайдера OAuth.

Регистрация заявления у провайдера идентификации

Не все провайдеры идентификации поддерживают индивидуальные требования, но если поддержка всё-таки есть, то процесс довольно похож. Вот как это делается с помощью Okta:

  1. Добавьте приложение OAuth в Okta и установите доверие к нему у поставщика OIDC в step-ca, как описано в статье DIY SSO for SSH.


  2. Добавьте новое поле в каталог пользователей Okta (например, GitHub Username)
  3. Добавьте к OIDC-токену индивидуальное требование, например, с сокращённым названием ghu
  4. Теперь заполняем поле для тестового юзера и проверяем требование. В Okta есть инструмент тестирования токена ID. Или можно использовать step для проверки всего потока OAuth:

    OIDC_ENDPOINT="http://personeltest.ru/aways/[your organization].okta.com/oauth2/default/.well-known/openid-configuration"CLIENT_ID="[your OAuth client ID]"CLIENT_SECRET="[your OAuth client secret]"step oauth --oidc --provider $OIDC_ENDPOINT \    --client-id $CLIENT_ID --client-secret $CLIENT_SECRET \    --listen=":10000" --bare |step crypto jwt inspect --insecure
    

  5. Наконец, настройте step-ca для использования этого шаблона. Конфигурация поставщика должна ссылаться на файл шаблона:

    {  "provisioners": [    {      "type": "OIDC",      "name": "Okta",      "clientID": "[your OAuth client ID]",      "clientSecret": "[your OAuth client secret]",      "configurationEndpoint": "https://[your organization].okta.com/oauth2/default/.well-known/openid-configuration",      "listenAddress": ":10000",      "options": {        "ssh": {            "templateFile": "templates/certs/ssh/github.tpl"        }      }    },      ...  ]}
    

Что дальше


Мы добавили в документацию раздел о шаблонах SSH, где более подробно рассматриваются все параметры и переменные.

Если есть вопросы не стесняйтесь задавать.
Подробнее..

Перевод Как я угнал национальный домен Демократической Республики Конго

25.02.2021 16:13:17 | Автор: admin
Примечание: проблема решена. Сейчас национальный домен .cd уже не делегирует полномочия скомпрометированному нейм-серверу

TL;DR Представьте, что может произойти, если национальный домен верхнего уровня (ccTLD) суверенного государства попадет в чужие руки. Однако я (@Almroot) купил доменное имя, которое указано для делегирования NS в национальном домене Демократической Республики Конго (.cd), и временно принял более 50% всего DNS-трафика для этой TLD. На моём месте мог оказаться злоумышленник, который использовал бы эту возможность для MITM или других злоупотреблений.



Вступление


За неделю до Рождества я решил провести анализ всех записей NS для всех TLD в мире. И моё внимание привлекло одно обстоятельство. У домена scpt-network.com был указан код статуса EPP redemptionPeriod. Это означает, что владелец не перечислил деньги за продление домена. Если владелец продолжит игнорировать оплату, то у него отберут собственность и домен поступит в свободную продажу.

Довольно проблематичная ситуация, поскольку он входит в список NS-серверов, управляющих зоной .cd:

almroot@x:~$ dig NS +trace cd | grep "cd."cd.172800INNSns-root-5.scpt-network.com.cd.172800INNSigubu.saix.net.cd.172800INNSsangoma.saix.net.cd.172800INNSns-root-2.scpt-network.com.cd.172800INNSsabela.saix.net.cd.172800INNSns-root-1.scpt-network.com.

Я решил на всякий случай написать bash-скрипт, который уведомит меня при любом изменении EPP-статуса.

К моему удивлению, примерно через неделю пришло уведомление, что домен перешёл в статус pendingDelete.

Я осознавал всю серьёзность ситуации. Доменное имя вскоре выставят на продажу для всех желающих, то есть любой человек может элементарно завладеть нейм-сервером .cd.

Я изменил скрипт и начал поминутно пинговать регистратора на предмет дальнейших изменений статуса.

Вечером 30 декабря пришло уведомление. Я открыл ноутбук и купил доменное имя, чтобы оно не попало в чужие руки.



Поскольку оставались ещё три записи делегирования на SAIX (South African Internet eXchange, Южноафриканская точка обмена трафиком), национальный домен сохранил работоспособность (хотя скорость резолвинга любых доменов немного уменьшилась).

Завладев scpt-network.com, я мог настроить любой поддомен на своё усмотрение. Например, если создать новый поддомен ns-root-1 с A-записью, которая указывает на IP-адрес 1.3.3.7, то на этот адрес 1.3.3.7 пойдут DNS-запросы для зоны .cd. Любой DNS-ответ на эти запросы будет принят как легитимный.



Если не ответить на запрос, то абонент словит таймаут с кодом состояния SERVFAIL. Это хорошо, потому что при получении такого кода он попытается связаться с любым другим сервером имён (NS record) для этой зоны. Так он в конечном итоге попадёт в одну из нормальных записей SAIX и будет соответствующим образом перенаправлен в нужное место назначения.

Потенциальное влияние


Захват национального домена верхнего уровня суверенного государства имеет серьёзные негативные последствия, особенно если этот домен попадёт в руки киберпреступников или иностранного противника. Демократическая Республика Конго (ДРК) немаленькая страна. Это примерно 90 миллионов человек, не говоря о многочисленных международных компаниях и организациях, работающих в этой доменной зоне.

Угон DNS-записи TLD целой страны явление редкое, но не новое. Например, десять лет назад киберпреступники захватили домен бывшего Советского Союза (.su), а в 2015 году вьетнамские сайты Lenovo и Google (.vn) также стали жертвами угона DNS. Перенаправление DNS-трафика с легальных сайтов .cd на фишинговый сайт одна из очевидных возможностей для злоупотреблений, но есть и другие:

  • Пассивный перехват DNS-трафика
    для слежки или эксфильтрации данных
  • Создание новых доменных имён из воздуха
    представьте себе возможности для быстрой генерации новых доменных имён с переключением ботнета на новые командные серверы каждые несколько минут, так что их никто не успевает заблокировать (техника fast flux)
  • Атаки с удалённым выполнением кода (RCE) в локальных сетях
    жертвами станут компании, которые используют протокол WPAD (протокол автоматической настройки прокси)
  • Поддельные DNS-ответы на законные DNS-запросы
    полный захват корневых доменов в зоне .cd или проведение DDoS-атаки.

Например, я мог написать эксплоит для захвата конкретного домена в зоне .cd. Представьте, что для любых NS-запросов к google.cd я всегда возвращаю ответы ns-root-1.scpt-network.com (вместо этих четырёх: [ns1,ns2,n3,ns4].google.com). Абонент получит такой ответ, и отправит любые последующие DNS-запросы к ns-root-1.scpt-network.com.

Это также заставило меня задуматься: а что, если отвечать на все NS-запросы ссылкой на себя. Тогда для любого запроса, на который ответит 1.3.3.7, все поиски домена в конечном итоге пойдут по этой ссылке. И весь последующий сетевой трафик будет перенаправлен на 1.3.3.7, что может привести к DDoS-атаке.

На самом деле это также повлияет на доступность всей TLD, ведь 50% DNS-ответов станут неправильными. Силу (обеих) DoS-атак можно увеличить путём установки высокого TTL в ответах DNS.

Сделаем ещё один шаг вперёд. Скажем, я конкретно подделываю TXT-записи для сервера google.cd. С помощью поддельных текстовых файлов я обманываю
систему проверки DNS-01 у регистратора Lets Encrypt и получаю действительный сертификат для google.cd, после чего эффективно взламываю зашифрованный канал SSL/TLS.

Поскольку я могу контролировать делегирование NS-серверов для любого домена .cd и получить валидные сертификаты, то могу провести MITM-атаку даже если жертва использует SSL/TLS.

Примечание переводчика. Некоторые описанные методы доступны государственным службам, которые контролируют национальные домены в своих странах.

В то время как Google применяет различные контрмеры против таких злоупотреблений, можно с уверенностью сказать, что это не относится ко всем корневым доменам в зоне .cd. Дополнительную информацию о том, как удостоверяющие центры проверяют право собственности на домены, см. в BR 1.7.3.

И последнее, но не менее важное: имея привилегированный доступ на вышестоящий хост с контролем DNS, я могу проникнуть в локальные сети компаний (пример на скриншоте ниже), которые отправляют DNS-запросы для WPAD можно отслеживать их запросы, подделать ответы и перенаправить жертву для загрузки и выполнения вредоносной конфигурации прокси-сервера на JS. У протокола WPAD своя куча проблем, включая уязвимости RCE, как рассказывали хакеры из команды Project Zero в Google.



Исправление проблемы


7 января 2021 года я связался с административными и техническими контактами, указанными для зоны .cd на странице IANA. Первоначально я хотел передать домен оператору .cd.

Хотя один из контактов ответил и направил меня к коллеге, на момент написания этой статьи я не получил письменного подтверждения, что они устранили проблему. Но вскоре DNS-трафик перенаправили на scpt-network.net.

8 января я также отправил отчёт по программе Internet Bug Bounty в HackerOne, которая предлагает вознаграждение за ответственный взлом инфраструктуры интернета.

Заключение


Угон DNS-сервера несёт крайне негативные последствия, особенно если у злоумышленника плохие намерения. Эта уязвимость затрагивает не только один сайт, поддомен или один корневой домен. Жертвой фишинга, MITM или DDoS может стать абсолютно любой сайт .cd, включая сайты крупных международных компаний, финансовых учреждений и других организаций. А это вторая по численности страна Африки и популярная доменная зона.

На момент написания этой статьи я всё ещё владею доменным именем scpt-network.com хотя делегирование NS-запросов из зоны .cd прекратились примерно 8 января 2021 года после того, как я с ними связался. Эту операцию я провёл для того, чтобы предотвратить захват злоумышленниками доменной зоны Демократической Республики Конго, когда любой желающий мог угнать доменное имя одного из серверов, управляющих ccTLD. К счастью, в этом случае всё обошлось.
Подробнее..

PKCS11 для самых маленьких

03.03.2021 08:18:42 | Автор: admin

В этой статье мы познакомимся со стандартом PKCS#11, предназначенным для работы с различными криптографическими устройствами. Для демонстрации мы будем использовать токены и смарт-карты Рутокен ЭЦП 2.0.

Чтобы вам было проще понять эту информацию, перед её прочтением желательно:

Исходя из определения из Википедии:

PKCS #11 один из стандартов семейства Public-Key Cryptography Standards (PKCS). Он определяет платформонезависимый программный интерфейс доступа к криптографическим устройствам смарт-картам, токенам, криптографическим ускорителям, серверам ключей и другим средствам криптографической защиты информации. Иногда именуется Cryptoki.

Проще говоря, PKCS#11 определяет платформонезависимый набор функций, структуры, константы и т.п. для работы с криптографическими устройствами. Эти функции могут быть реализованы внутри различных библиотек, например, opensc-pkcs11 или в нашей библиотеке по работе с устройствами Рутокен rtpkcs11ecp. Библиотеки могут отличаться не только реализацией, но и самим набором функций, типов и констант. Это возможно, так как стандарт PKCS#11 описывает различные способы расширения, что позволяет добавлять свои функции, например, для работы с CMS-подписью, флеш-памятью и т.п.

Но давайте обо всем по порядку. В первую очередь, определимся, что из себя представляют функции PKCS#11-библиотек. Функции PKCS#11 внутри это обертки для работы с токенами и смарт-картами через APDU команды

Про APDU

APDU язык ассемблера для устройств.

Более подробно об APDU-формате написано в этой статье, мы же опишем его проще. APDU-формат описывает способ общения с различными устройствами с помощью байтовых последовательностей. На практике это происходит следующим образом:

  1. Токену или смарт-карте посылается байтовая последовательность;

  2. Операционная система устройства обрабатывает эту последовательность в команду и посылает в ответ код возврата и, при необходимости, дополнительную информацию.

Что же могут содержать внутри себя такие команды? Все, что угодно! Начиная с просьбы отправить информацию об устройстве или записать какие-то данные на него и заканчивая просьбой зашифровать указанное сообщение. Более того, эти команды могут быть разбиты на несколько, что позволяет оптимизировать работу с токеном и сделать ее конвейерной. К примеру, при шифровании сообщения устройству может передаваться небольшая часть сообщения, а он в ответ будет высылать результат шифрования только этой части. Эта особенность может быть использована при работе с данными больших размеров.

И вот вроде бы все хорошо: APDU даёт полную возможность для общения с токеном и смарт-картой, но зачем же тогда нужна PKCS#11-обертка? Причины достаточно очевидны:

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

  • Программирование через APDU-команды имеет низкий КПД. При выполнении даже самой простой операции надо написать множество команд. PKCS#11 устраняет эту проблему: при выполнении одной функции PKCS#11 посылается несколько APDU команд. Меньше пишешь больше делаешь.

Теперь, когда мы знаем что лежит в основе PKCS#11-команд, перейдем к рассмотрению цикла работы с устройством.

Репозиторий с примерами

Чтобы улучшить понимание представленных ниже примеров рекомендуем:

  1. Клонировать репозиторий с примерами.

  2. Выполнить настройку системы согласно README.

  3. Попробовать собрать примеры.

  4. Отформатировать устройство семейства Рутокен ЭЦП 2.0 с помощью примера ex0_format_token.

Цикл работы с токеном

Придержим пока описание содержимого стандарта PKCS#11 и дадим поверхностное представление о том, как происходит работа с токеном в целом. Для этого рассмотрим листинг смены PIN-кода Пользователя на токене и смарт-карте:

#include #include "utils.h"extern CK_FUNCTION_LIST_PTR functionList;                 // Указатель на список функций PKCS#11, хранящийся в структуре CK_FUNCTION_LISTextern CK_FUNCTION_LIST_EXTENDED_PTR functionListEx;      // Указатель на список функций расширения PKCS#11, хранящийся в структуре CK_FUNCTION_LIST_EXTENDEDint change_pin_code(CK_SLOT_ID slot, char* oldPin, char* newPin);int main(void){    CK_SLOT_ID_PTR slots;                              // Массив идентификаторов слотов    CK_ULONG slotCount;                                // Количество идентификаторов слотов в массиве    char* oldPin = "12345678";    char* newPin = "12345678";    CK_RV rv;                                          // Код возврата. Могут быть возвращены только ошибки, определенные в PKCS#11    int errorCode = 1;                                 // Флаг ошибки    // инициализируем библиотеку    if (init_pkcs11())         goto exit;    // получаем список слотов    if (get_slot_list(&slots, &slotCount))        goto free_pkcs11;    if (slotCount == 0) {        printf("No token found\n");        goto free_slots;    }    // изменяем PIN-код    if (change_pin_code(slots[0], oldPin, newPin))        goto free_slots;    errorCode = 0;    /*************************************************************************    * Очистить память, выделенную под слоты                                  *    *************************************************************************/free_slots:    free(slots);    /*************************************************************************    * Деинициализировать библиотеку                                          *    *************************************************************************/free_pkcs11:    free_pkcs11();exit:    if (errorCode) {        printf("\n\nSome error occurred. Sample failed.\n");    } else {        printf("\n\nSample has been completed successfully.\n");    }    return errorCode;}

Внутри заголовочного файла utils.h находится описание функций init_pkcs11, free_pkcs11, get_slot_list. Их определение мы дадим ниже.

Всю работу с токеном можно разделить на три этапа:

Подготовительный этап

Мы реализовали его внутри функции init_pkcs11:

#include "utils.h"CK_FUNCTION_LIST_PTR functionList;                 // Указатель на список функций PKCS#11, хранящийся в структуре CK_FUNCTION_LISTCK_FUNCTION_LIST_EXTENDED_PTR functionListEx;      // Указатель на список функций расширения PKCS#11, хранящийся в структуре CK_FUNCTION_LIST_EXTENDEDstatic HMODULE module;int init_pkcs11(){    CK_C_GetFunctionList getFunctionList;              // Указатель на функцию C_GetFunctionList    CK_C_EX_GetFunctionListExtended getFunctionListEx; // Указатель на функцию C_EX_GetFunctionListExtended    /* Параметры для инициализации библиотеки: разрешаем использовать объекты синхронизации операционной системы */    CK_C_INITIALIZE_ARGS initArgs = { NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, CKF_OS_LOCKING_OK, NULL_PTR };    CK_RV rv;                      // Код возврата PKCS#11 функций    int errorCode = 1;                                 // Флаг ошибки    /*************************************************************************    * Выполнить действия для начала работы с библиотекой PKCS#11             *    *************************************************************************/    printf("Initialization...\n");    /*************************************************************************    * Загрузить библиотеку                                                   *    *************************************************************************/    module = LoadLibrary(PKCS11_LIBRARY_DIR "/" PKCS11ECP_LIBRARY_NAME);    CHECK(" LoadLibrary", module != NULL, exit);    /*************************************************************************    * Получить адрес функции запроса структуры с указателями на функции      *    *************************************************************************/    getFunctionList = (CK_C_GetFunctionList)GetProcAddress(module, "C_GetFunctionList");    CHECK(" GetProcAddress (C_GetFunctionList)", getFunctionList != NULL, unload_pkcs11);    /*************************************************************************    * Получить адрес функции запроса структуры с указателями на функции      *    * расширения стандарта PKCS#11                                           *    *************************************************************************/    getFunctionListEx = (CK_C_EX_GetFunctionListExtended)GetProcAddress(module, "C_EX_GetFunctionListExtended");    CHECK(" GetProcAddress (C_EX_GetFunctionListExtended)", getFunctionList != NULL, unload_pkcs11);    /*************************************************************************    * Получить структуру с указателями на функции                            *    *************************************************************************/    rv = getFunctionList(&functionList);    CHECK_AND_LOG(" Get function list", rv == CKR_OK, rvToStr(rv), unload_pkcs11);    /*************************************************************************    * Получить структуру с указателями на функции расширения стандарта       *    *************************************************************************/    rv = getFunctionListEx(&functionListEx);    CHECK_AND_LOG(" Get function list extended", rv == CKR_OK, rvToStr(rv), unload_pkcs11);    /*************************************************************************    * Инициализировать библиотеку                                            *    *************************************************************************/    rv = functionList->C_Initialize(&initArgs);    CHECK_AND_LOG(" C_Initialize", rv == CKR_OK, rvToStr(rv), unload_pkcs11);    errorCode = 0;    /*************************************************************************    * Выгрузить библиотеку из памяти                                         *    *************************************************************************/unload_pkcs11:    if (errorCode)        CHECK_RELEASE(" FreeLibrary", FreeLibrary(module), errorCode);exit:    return errorCode;}

Здесь происходит следующее:

  1. В память процесса подгружается PKCS#11-библиотека, хранящаяся по пути PKCS11ECP_LIBRARY_NAME, с помощью функции LoadLibrary (стандартная функция для Windows-систем, для Linux-систем определена обертка).

  2. Далее из библиотеки вытаскиваются указатели на функции C_GetFunctionList и C_EX_GetFunctionListExtended. Первая функция определена в стандарте PKCS#11 и позволяет получить структуру указателей на функции библиотеки. Вторая является специфичной для библиотеки rtpkcs11ecp и позволяет получить схожую структуру указателей на функции расширения библиотеки. О функциях расширения мы поговорим позже.

  3. Потом мы вызываем полученную функцию C_GetFunctionList и получаем уже саму структуру указателей на функции.

  4. С помощью функции C_Initialize инициализируется загруженная библиотека. Функция C_Initialize в качестве аргумента принимает параметры инициализации библиотеки. Подробнее о них можно почитать здесь, для нас же важен флаг CKF_OS_LOCKING_OK. Его необходимо использовать, если мы хотим использовать библиотеку в нескольких потоках. В нашем примере мы могли бы опустить этот флаг.

Основной этап

Основной этап можно разделить на работу со слотами и работу внутри сессии

Этап работы со слотами

Слот это дескриптор виртуального интерфейса, куда подключен токен. Конкретно в нашем примере весь этап работы со слотами уместился в определение одной функции get_slot_list:

int get_slot_list(CK_SLOT_ID_PTR* slots_ptr, CK_ULONG_PTR slotCount){    CK_RV rv;    int errorCode = 1;    /*************************************************************************    * Получить количество слотов c подключенными токенами                    *    *************************************************************************/    rv = functionList->C_GetSlotList(CK_TRUE, NULL_PTR, slotCount);    CHECK_AND_LOG(" C_GetSlotList (number of slots)", rv == CKR_OK, rvToStr(rv), exit);    CHECK_AND_LOG(" Checking available tokens", *slotCount > 0, " No tokens available", exit);    /*************************************************************************    * Получить список слотов c подключенными токенами                        *    *************************************************************************/    *slots_ptr = (CK_SLOT_ID_PTR)malloc(*slotCount * sizeof(CK_SLOT_ID));    CHECK(" Memory allocation for slots", *slots_ptr != NULL_PTR, exit);    rv = functionList->C_GetSlotList(CK_TRUE, *slots_ptr, slotCount);    CHECK_AND_LOG(" C_GetSlotList", rv == CKR_OK, rvToStr(rv), free_slots);    printf(" Slots available: %d\n", (int)*slotCount);    /*************************************************************************    * Выставить признак успешного завершения программы                       *    *************************************************************************/    errorCode = 0;free_slots:    if (errorCode)    {        free(*slots_ptr);    }exit:    return errorCode;}

Работа со слотами происходит примерно в такой последовательности:

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

  2. Выполнение различных функций работы со слотами. В нашей программе этот этап отсутствует, но его пример можно найти здесь для функции форматирования устройства C_EX_InitToken. Подробнее функции работы со слотами будут описаны ниже.

Этап работы внутри сессии

Сессия это дескриптор контекста выполнения последовательности операций. Обычно во время сессий выполняются основные функции работы с токеном или смарт-картой: шифрование, подпись и т.п. В нашем случае выполняется смена PIN-кода:

int change_pin_code(CK_SLOT_ID slot, char* oldPin, char* newPin){    CK_SESSION_HANDLE session;                         // Хэндл открытой сессии    CK_RV rv;                                          // Код возврата. Могут быть возвращены только ошибки, определенные в PKCS#11    int errorCode = 1;    /*************************************************************************    * Открыть RW сессию в первом доступном слоте                             *    *************************************************************************/    rv = functionList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &session);    CHECK_AND_LOG(" C_OpenSession", rv == CKR_OK, rvToStr(rv), exit);    /*************************************************************************    * Выполнить аутентификацию Пользователя                                *    *************************************************************************/    rv = functionList->C_Login(session, CKU_USER, oldPin, strlen(oldPin));    CHECK_AND_LOG(" C_Login (CKU_USER)", rv == CKR_OK, rvToStr(rv), close_session);    /*************************************************************************    * Установить PIN-код Пользователя по умолчанию                           *    *************************************************************************/    printf("\nChanging user PIN to default...\n");    rv = functionList->C_SetPIN(session, NULL_PTR, 0, newPin, strlen(newPin));    CHECK_AND_LOG(" C_SetPIN", rv == CKR_OK, rvToStr(rv), logout);    printf("User PIN has been changed to default successfully.\n");    errorCode = 0;    /*************************************************************************    * Сбросить права доступа                                                 *    *************************************************************************/logout:    rv = functionList->C_Logout(session);    CHECK_RELEASE_AND_LOG(" C_Logout", rv == CKR_OK, rvToStr(rv), errorCode);    /*************************************************************************    * Закрыть открытую сессию в слоте                                        *    *************************************************************************/close_session:    rv = functionList->C_CloseSession(session);    CHECK_RELEASE_AND_LOG(" C_CloseSession", rv == CKR_OK, rvToStr(rv), errorCode);exit:    return errorCode;}

Работу внутри сессий можно разделить на несколько этапов:

  1. Открытие сессии в слоте с помощью функции C_OpenSession.

  2. Аутентификация пользователя с помощью функции C_Login. Стандартно на Рутокене присутствуют два пользователя: Администратор (CKU_SO) и Пользователь (CKU_USER). Аутентификация на устройстве не является обязательной операцией. Она нужна, когда мы хотим получить доступ к операциям и объектам, которые требуют наличия соответствующих прав доступа. В нашем случае, это операция смены PIN-кода Пользователя.

  3. Выполнение различных функций по работе с сессиями. В нашем случае это функция C_SetPIN.

  4. Далее по нисходящей могут идти операции сброса прав доступа (C_Logout) и завершения сессии (C_CloseSession).

Завершающий этап

Весь наш завершающий этап поместился внутри определения функции free_pkcs11:

int free_pkcs11(){        CK_RV rv;        int errorCode;        printf("\nFinalizing... \n");    rv = functionList->C_Finalize(NULL_PTR);        CHECK_RELEASE_AND_LOG(" C_Finalize", rv == CKR_OK, rvToStr(rv), errorCode);    CHECK_RELEASE(" FreeLibrary", FreeLibrary(module), errorCode);        return errorCode;}

Завершающий этап можно разделить на:

  1. Деинициализацию библиотеки с помощью функции C_Finalize.

  2. Выгрузку библиотеки из памяти процесса через функцию FreeLibrary (для Linux-систем имеется обертка).

Классификация функций PKCS#11

Стандартные функции и функции расширения

В общем виде все функции внутри PKCS#11-библиотек можно разделить на:

  • Стандартные те, что явно описаны в стандарте. Список указателей на эти функции можно получить с помощью функции C_GetFunctionList, хранящейся в этой же библиотеке. Сами же стандартные функции имеют префикс "C_".

  • Расширения те, что были добавлены разработчиками библиотеки и не описаны в стандарте. Стандарт явно не определяет функцию для получения списка расширенных функций. Но в библиотеке rtpkcs11ecp этой функцией является C_EX_GetFunctionListExtended. Сами же функции-расширения обычно имеют префикс "C_EX_".

Описание стандартных функций можно найти в документации PKCS#11. Про то, как работать с расширениями надо смотреть у разработчиков конкретных библиотек. Например, у нас для этого есть отдельная страница на портале документации.

Разделение по предназначению

С другой стороны, функции PKCS#11 можно классифицировать по их предназначению. Логично выделить следующие виды функций:

  • Функции общего назначения. Например, для получения списка функций или получения информации о библиотеке.

  • Функции работы со слотами. Они не зависят от контекста работы с токеном. Например, C_GetSlotInfo, C_GetTokenInfo и т.п.

  • Функции для работы с сессиями. Как уже говорилось ранее, обычно во время выполнения этих функций осуществляется основное взаимодействие с токеном. При выполнении функции внутри сессии контекст работы с токеном не теряется. Самый простой пример этих функций: функция аутентификации (C_Login), функции работы с закрытым ключом на токене (C_Sign, C_Encrypt) и т.п.

Атомарные и составные операции

Некоторые операции в PKCS#11 работают привычным для нас образом: вызвал функцию получил результат. Но есть и операции, реализация которых выстроена через многократный вызов функций. Ярким примером такой операции является блочное шифрование: чтобы зашифровать данные нужно вызвать функции: C_EncryptInit, C_EncryptUpdate, C_EncryptFinish. Пример этого будет приведен ниже. Эта особенность связана с тем, что внутри библиотеки PKCS#11 скрыт вызов APDU-команд, который как раз предусматривает разбиение некоторых команд на несколько итераций. Как уже говорилось ранее, это способствует эффективному использованию PKCS#11 с потоками данных.

Работа со слотами

Слоты это виртуальные устройства библиотеки для подключения токенов и смарт-карт. PKCS#11 предоставляет функции для получения списка слотов и ожидания изменения состояний слотов. Также с помощью специальных функций можно получать информацию о состоянии слота, например, наличие токена в нем, информация о подключенном токене и т.п. Давайте подробнее рассмотрим функции по работе со слотами.

Получение списка слотов

Одна из самых важных операций, которую мы будем использовать в 99 процентах случаев это получение списка активных слотов. Для этого в PKCS#11 есть функция C_GetSlotList. Примером ее использования является функция get_slot_list, определенная ниже:

int get_slot_list(CK_SLOT_ID_PTR* slots_ptr, CK_ULONG_PTR slotCount){    CK_RV rv;    int errorCode = 1;    /*************************************************************************    * Получить количество слотов c подключенными токенами                    *    *************************************************************************/    rv = functionList->C_GetSlotList(CK_TRUE, NULL_PTR, slotCount);    CHECK_AND_LOG(" C_GetSlotList (number of slots)", rv == CKR_OK, rvToStr(rv), exit);    CHECK_AND_LOG(" Checking available tokens", *slotCount > 0, " No tokens available", exit);    /*************************************************************************    * Получить список слотов c подключенными токенами                        *    *************************************************************************/    *slots_ptr = (CK_SLOT_ID_PTR)malloc(*slotCount * sizeof(CK_SLOT_ID));    CHECK(" Memory allocation for slots", *slots_ptr != NULL_PTR, exit);    rv = functionList->C_GetSlotList(CK_TRUE, *slots_ptr, slotCount);    CHECK_AND_LOG(" C_GetSlotList", rv == CKR_OK, rvToStr(rv), free_slots);    printf(" Slots available: %d\n", (int)*slotCount);    /*************************************************************************    * Выставить признак успешного завершения программы                       *    *************************************************************************/    errorCode = 0;free_slots:    if (errorCode)    {        free(*slots_ptr);    }exit:    return errorCode;}

Первый вызов функции C_GetSlotList позволяет получить количество доступных слотов. Это позволяет в дальнейшем выделять память под необходимое количество слотов. Второй вызов позволяет получить список слотов.

Первым аргументом C_GetSlotList является флаг, говорящий библиотеке, возвращать ли только слоты с подключенными устройствами (CK_TRUE) или нет (CK_FALSE).

Мониторинг событий слотов и получение информации о слоте

Тем не менее, хоть функция C_GetSlotList и является самой часто используемой, она неудобна при написании приложений, которые хотят динамически следить за состоянием слотов. Особенно это может быть критично при написании многопоточных приложений. На помощь может прийти функция мониторинга событий слотов C_WaitForSlotEvent. Она прерывает свою работу при любом изменении состояния какого-либо слота и возвращает идентификатор измененного слота. Работа с этой функцией выглядит примерно следующим образом:

int monitor_slot_event(){    int errorCode = 0;    while (1) {        CK_SLOT_ID slot ;            CK_RV rv = functionList->C_WaitForSlotEvent(0, &slot, NULL_PTR);        if (CKR_CRYPTOKI_NOT_INITIALIZED == rv) break; // Индикатор того, что PKCS#11 деинициализирована из памяти.        CHECK_RELEASE_AND_LOG(" C_WaitForSlotEvent", rv == CKR_OK, rvToStr(rv), errorCode);        if (errorCode)            break;        CK_SLOT_INFO slotInfo;        rv = functionList->C_GetSlotInfo(slot, &slotInfo); // получение информации о слоте        if (CKR_CRYPTOKI_NOT_INITIALIZED == rv) break; // Индикатор того, что PKCS#11 деинициализирована из памяти.        CHECK_RELEASE_AND_LOG(" C_GetSlotInfo", rv == CKR_OK, rvToStr(rv), errorCode);        if (errorCode)            break;        if (CKF_TOKEN_PRESENT & slotInfo.flags) {                     token_inserted(slot);        }    }}

Как можно заметить, функция отлавливает помимо событий подключения и извлечения токена и смарт-карты, еще и событие деинициализации библиотеки PKCS#11 (код возврата CKR_CRYPTOKI_NOT_INITIALIZED). Данная особенность позволяет использовать эту функцию внутри многопоточных приложений без дополнительной возни с обработкой событий при завершении работы приложения.

Первым аргументом в функцию C_WaitForSlotEvent передается флаг блокировки (CKF_DONT_BLOCK). Если он установлен, то функция не является блокирующей. В таком случае, если никакой слот не был изменен, то возвращается код CKR_NO_EVENT.

Также стоит обратить внимание на использование функции получения информации о слоте C_GetSlotInfo. Мы использовали ее для определения наличия токена по флагу CKF_TOKEN_PRESENT. Подробнее с этой структурой можно ознакомиться здесь.

В этом файле представлен пример получения информации о подключенных токенах или смарт-картах с помощью функции C_GetTokenInfo. С помощью этой функции можно получить серийный номер, метку и другие характеристики подключенного устройства.

Объекты

Всё есть объект это почти про PKCS#11.

Теперь начнём знакомство с PKCS#11-объектами. А чтобы закрепить материал по функциям, будем рассматривать множество примеров с их использованием. Поскольку одним из самых важных объектов, которые обычно хранятся на токене или смарт-карте является ключевая пара, мы начнём знакомство с объектами через генерацию ключевой пары.

Создание объектов на примере генерации ключевых пар

В первую очередь, напишем функцию, которая будет генерировать ключевую пару ГОСТ Р 34.10-2012 256 бит на указанном слоте:

int gen_gost_key_pair(CK_SESSION_HANDLE session){    CK_KEY_TYPE keyTypeGostR3410_2012_256 = CKK_GOSTR3410;    CK_BYTE keyPairIdGost2012_256[] = { "GOST R 34.10-2012 (256 bits) sample key pair ID (Aktiv Co.)" };    CK_BYTE parametersGostR3410_2012_256[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 };    CK_BYTE parametersGostR3411_2012_256[] = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02 };    CK_BBOOL attributeTrue = CK_TRUE;    CK_BBOOL attributeFalse = CK_FALSE;    CK_OBJECT_CLASS publicKeyObject = CKO_PUBLIC_KEY;    CK_ATTRIBUTE publicKeyTemplate[] =    {        { CKA_CLASS, &publicKeyObject, sizeof(publicKeyObject)},                                        // Класс - открытый ключ        { CKA_ID, &keyPairIdGost2012_256, sizeof(keyPairIdGost2012_256) - 1 },                          // Идентификатор ключевой пары (должен совпадать у открытого и закрытого ключей)        { CKA_KEY_TYPE, &keyTypeGostR3410_2012_256, sizeof(keyTypeGostR3410_2012_256) },                // Тип ключа - ГОСТ Р 34.10-2012(256)        { CKA_TOKEN, &attributeTrue, sizeof(attributeTrue)},                                            // Ключ является объектом токена        { CKA_PRIVATE, &attributeFalse, sizeof(attributeFalse)},                                        // Ключ доступен без аутентификации на токене        { CKA_GOSTR3410_PARAMS, parametersGostR3410_2012_256, sizeof(parametersGostR3410_2012_256) },   // Параметры алгоритма ГОСТ Р 34.10-2012(256)        { CKA_GOSTR3411_PARAMS, parametersGostR3411_2012_256, sizeof(parametersGostR3411_2012_256) }    // Параметры алгоритма ГОСТ Р 34.11-2012(256)    };    CK_OBJECT_CLASS privateKeyObject = CKO_PRIVATE_KEY;    CK_ATTRIBUTE privateKeyTemplate[] =    {        { CKA_CLASS, &privateKeyObject, sizeof(privateKeyObject)},                                      // Класс - закрытый ключ        { CKA_ID, &keyPairIdGost2012_256, sizeof(keyPairIdGost2012_256) - 1 },                          // Идентификатор ключевой пары (должен совпадать у открытого и закрытого ключей)        { CKA_KEY_TYPE, &keyTypeGostR3410_2012_256, sizeof(keyTypeGostR3410_2012_256) },                // Тип ключа - ГОСТ Р 34.10-2012(256)        { CKA_TOKEN, &attributeTrue, sizeof(attributeTrue)},                                            // Ключ является объектом токена        { CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},                                          // Ключ доступен только после аутентификации на токене        { CKA_GOSTR3410_PARAMS, parametersGostR3410_2012_256, sizeof(parametersGostR3410_2012_256) },   // Параметры алгоритма ГОСТ Р 34.10-2012(256)        { CKA_GOSTR3411_PARAMS, parametersGostR3411_2012_256, sizeof(parametersGostR3411_2012_256) }    // Параметры алгоритма ГОСТ Р 34.11-2012(256)    };    CK_OBJECT_HANDLE privateKey;                      // Хэндл закрытого ключа ГОСТ (ключевая пара для подписи и шифрования)        CK_OBJECT_HANDLE publicKey;                       // Хэндл открытого ключа ГОСТ (ключевая пара для подписи и шифрования)        CK_MECHANISM gostR3410_2012_256KeyPairGenMech = { CKM_GOSTR3410_KEY_PAIR_GEN, NULL_PTR, 0 };    CK_RV rv;       int errorCode = 1;    /*************************************************************************    * Генерация ключевой пары на токене                                      *    *************************************************************************/    rv = functionList->C_GenerateKeyPair(session, &gostR3410_2012_256KeyPairGenMech,     publicKeyTemplate, arraysize(publicKeyTemplate),    privateKeyTemplate, arraysize(privateKeyTemplate),    &publicKey, &privateKey);    CHECK_AND_LOG(" C_GenerateKeyPair", rv == CKR_OK, rvToStr(rv), exit);    errorCode = 0;    printf("Gost key pair generated successfully\n");exit:    return errorCode;}

В этом примере для нас много нового. Можно заметить, что здесь вызывается всего одна функция C_GenerateKeyPair. Эта функция является стандартной функцией генерации ключей, работающей внутри открытой сессии. Также стоит отметить, что пользователь должен быть аутентифицирован перед вызовом этой функции.

Теперь перейдём к объектам. Внутри функции gen_gost_key_pair происходит создание двух объектов на токене: открытого и закрытого ключей. Вот, что стандарт PKCS#11 говорит про объекты:

Cryptoki recognizes a number of classes of objects, as defined in the CK_OBJECT_CLASS data type. An object consists of a set of attributes, each of which has a given value. Each attribute that an object possesses has precisely one value.

То есть стандарт не даёт явное определение объекта, но из того, что там написано, мы знаем:

  • объект относится к определенному классу;

  • объект состоит из множества атрибутов, имеющих заданное значение;

  • каждый атрибут имеет ровно одно значение.

Также в стандарте представлена классификация объектов:

Иерархия PKCS#11 объектовИерархия PKCS#11 объектов

Заголовок диаграммы определяет класс объекта, а то что ниже некоторые из его атрибутов.
Видно, что объектом может являться некоторый механизм (о механизмах мы поговорим позже), встроенные функции токена (Hardware feature), некоторые данные на токене (Storage). В нашем случае мы выполнили действие с данными.

Название всех атрибутов начинается с префикса "CKA_". Одним из самых важных атрибутов является CKA_ID. Он задаёт идентификатор объекта и используется для связи ключевых пар и сертификатов. Атрибут CKA_TOKEN является булевым и показывает, является ли объект объектом токена. Атрибут CKA_PRIVATE тоже является булевым и определяет нужна ли предварительная аутентификация для получения доступа к объекту. Атрибут CKA_ID задаёт шестнадцатеричный идентификатор объекта. Также есть булевые атрибуты CKA_MODIFIABLE, CKA_COPYABLE, CKA_DESTROYABLE для более тонкой настройки доступа к объекту. Подробнее про возможные атрибуты конкретных классов объектов можно прочитать непосредственно в самом стандарте для каждого класса объектов.

Объекты данных могут быть самыми разнообразными: асимметричные ключи, симметричные ключи, сертификаты, просто какая-либо информация на токене. В нашем примере мы создали два объекта, но сделали это неявно с помощью механизма генерации ключей. C_GenerateKeyPair приняла на вход механизм генерации ключевой пары, шаблоны открытого и закрытого ключа и с помощью механизма сгенерировала объекты ключевой пары (publicKey и privateKey). Мы пока ещё не описывали механизмы, но, говоря простым языком, механизм это идентификатор операции, которая выполняет какую-то криптографическую функцию. В нашем случае это функция генерации объекта.

Поиск объектов и создание сырой подписи

В прошлом разделе мы сгенерировали ключевую пару. На этот раз будем считать, что у нас нет хендлов на сгенерированные ключи, но мы знаем их идентификатор CKA_ID. Попробуем найти объект закрытого ключа на токене:

int findObjects(CK_SESSION_HANDLE session,         // Хэндл открытой сессии                CK_ATTRIBUTE_PTR attributes,       // Массив с шаблоном для поиска                CK_ULONG attrCount,                // Количество атрибутов в массиве поиска                CK_OBJECT_HANDLE objects[],        // Массив для записи найденных объектов                CK_ULONG* objectsCount             // Количество найденных объектов                       ){    CK_RV rv;                                           // Код возврата. Могут быть возвращены только ошибки, определенные в PKCS#11    int errorCode = 1;                                  // Флаг ошибки    /*************************************************************************    * Инициализировать операцию поиска                                       *    *************************************************************************/    rv = functionList->C_FindObjectsInit(session, attributes, attrCount);    CHECK_AND_LOG("  C_FindObjectsInit", rv == CKR_OK, rvToStr(rv), exit);    /*************************************************************************    * Найти все объекты, соответствующие критериям поиска                    *    *************************************************************************/    rv = functionList->C_FindObjects(session, objects, *objectsCount, objectsCount);    CHECK_AND_LOG("  C_FindObjects", rv == CKR_OK, rvToStr(rv), find_final);    errorCode = 0;    /*************************************************************************    * Деинициализировать операцию поиска                                     *    *************************************************************************/find_final:    rv = functionList->C_FindObjectsFinal(session);    CHECK_RELEASE_AND_LOG("  C_FindObjectsFinal", rv == CKR_OK, rvToStr(rv), errorCode);exit:    return errorCode;}int find_private_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR privateKey){        CK_BYTE keyPairIdGost2012_256[] = { "GOST R 34.10-2012 (256 bits) sample key pair ID (Aktiv Co.)" };        CK_OBJECT_CLASS privateKeyObject = CKO_PRIVATE_KEY;        CK_ATTRIBUTE privateKeyTemplate[] =        {                { CKA_CLASS, &privateKeyObject, sizeof(privateKeyObject)},              // Класс - закрытый ключ                { CKA_ID, &keyPairIdGost2012_256, sizeof(keyPairIdGost2012_256) - 1},   // Идентификатор ключевой пары (должен совпадать у открытого и закрытого ключей)        };        CK_ULONG cnt = 1;        CK_RV rv;        int errorCode = 1;        rv = findObjects(session, privateKeyTemplate,        arraysize(privateKeyTemplate), privateKey, &cnt);        CHECK(" findObjects", rv == 0, exit);        CHECK_AND_LOG(" Checking number of keys found", cnt == 1, "No objects found\n", exit);        errorCode = 0;exit:        return errorCode;}

Данный пример иллюстрирует работу с функцией поиска объекта по заданным атрибутам. Как можно заметить, операция поиска объекта на токене является составной и работа с ней сводится как минимум к вызову трёх функций: C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal. Функция C_FindObjects может вызываться по несколько раз, и каждый раз она будет возвращать следующие объекты поиска. Предпоследний аргумент функции C_FindObjects задаёт размер выходного массива объектов. А последний количество полученных объектов после очередного поиска.

Поиск приватного ключа производился по атрибуту его класса и идентификатору. Мы рассчитывали, что найдётся хотя бы один объект по заданному шаблону и брали любой из них. Используем найденный ключ для вычисления сырой подписи:

int sign(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE privateKey){        /* OID алгоритма хеширования ГОСТ Р 34.11-2012(256)                     */    CK_BYTE parametersGostR3411_256[] = {0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02};    /* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2012(256) и хешированием по алгоритму ГОСТ Р 34.11-2012(256) */    CK_MECHANISM gost3410SignWith3411Mech = { CKM_GOSTR3410_WITH_GOSTR3411_12_256, metersGostR3411_256, sizeof(parametersGostR3411_256)};    CK_BYTE data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };    CK_BYTE_PTR signature;                            // Указатель на буфер, содержащий цифровую подпись для данных    CK_ULONG signatureSize;                           // Размер буфера, содержащего цифровую подпись для данных, в байтах    CK_RV rv;    int errorCode = 1;    /*************************************************************************    * Вычислить подпись от данных                                            *    *************************************************************************/    printf(" Signing data...\n");    /*************************************************************************    * Инициализировать операцию подписи данных                               *    *************************************************************************/    rv = functionList->C_SignInit(session, &gost3410SignWith3411Mech, privateKey);    CHECK_AND_LOG("  >C_SignInit", rv == CKR_OK, rvToStr(rv), exit);    /*************************************************************************    * Определить размер данных подписи                                       *    *************************************************************************/    rv = functionList->C_Sign(session, data, sizeof(data), NULL_PTR, &signatureSize);    CHECK_AND_LOG("  C_Sign(get size)", rv == CKR_OK, rvToStr(rv), exit);    /*************************************************************************    * Подписать данные                                                       *    *************************************************************************/    signature = (CK_BYTE*)malloc(signatureSize * sizeof(CK_BYTE));    CHECK("  Memory allocation for signature", signature != NULL, exit);    rv = functionList->C_Sign(session, data, sizeof(data), signature, &signatureSize);    CHECK_AND_LOG("  C_Sign (signing)", rv == CKR_OK, rvToStr(rv), free_signature);    /*************************************************************************    * Распечатать буфер, содержащий подпись                                  *    *************************************************************************/    printf("  Signature buffer is: \n");    printHex(signature, signatureSize);    printf("Data has been signed successfully.\n");    errorCode = 0;free_signature:    free(signature);exit:    return errorCode;}

В этом примере подпись и хеш можно считать одновременно. Такой вариант рекомендован для безопасности: цепочку "хеширование-подпись" лучше не разрывать. Чтобы показать, какой алгоритм хеширования использовать, мы передали его OID.

Также имеется возможность считать сырую подпись в два этапа: сначала брать хеш от данных, а затем вычислялась подпись от хеша. Такой подход более модульный, т.к. алгоритмы хеширования и вычисления подписи могут быть любыми и их можно комбинировать. Естественно, комбинировать можно с некоторыми ограничениями, которые налагаются стандартами, например, на длину хеша.

Работа с функциями расширения на примере создания запроса на сертификат

Пришло время показать, как работать с функциями-расширениями. Сделаем это на примере библиотеки PKCS#11 от Рутокен и создадим запрос на сертификат для нашей ключевой пары. Генерация запроса на сертификат не описана в стандарте, поэтому сделаем это через функцию-расширение C_EX_CreateCSR.

int create_csr(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE publicKey, CK_OBJECT_HANDLE privateKey){    /*************************************************************************    * Запрос на получение сертификата                                        *    *************************************************************************/    /*************************************************************************    * Список полей DN (Distinguished Name)                                   *    *************************************************************************/    CK_CHAR_PTR dn[] = { (CK_CHAR_PTR)"CN",                   // Тип поля CN (Common Name)                     (CK_CHAR_PTR)"UTF8String:Иванов",        // Значение                     (CK_CHAR_PTR)"C",                        // C (Country)                     (CK_CHAR_PTR)"RU",                     (CK_CHAR_PTR)"2.5.4.5",                  // SN (Serial Number)                     (CK_CHAR_PTR)"12312312312",                     (CK_CHAR_PTR)"1.2.840.113549.1.9.1",     // E (E-mail)                     (CK_CHAR_PTR)"ivanov@mail.ru",                     (CK_CHAR_PTR)"ST",                       // ST (State or province)                     (CK_CHAR_PTR)"UTF8String:Москва",                     (CK_CHAR_PTR)"O",                        // O (Organization)                     (CK_CHAR_PTR)"CompanyName",                     (CK_CHAR_PTR)"OU",                       // OU (Organizational Unit)                     (CK_CHAR_PTR)"Devel",                     (CK_CHAR_PTR)"L",                        // L (Locality)                     (CK_CHAR_PTR)"Moscow", };    /*************************************************************************    * Список дополнительных полей                                            *    *************************************************************************/    CK_CHAR_PTR exts[] = {(CK_CHAR_PTR)"keyUsage",                                                        // Использование ключа                      (CK_CHAR_PTR)"digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment",                      (CK_CHAR_PTR)"extendedKeyUsage",                                                    // Дополнительное использование                      (CK_CHAR_PTR)"1.2.643.2.2.34.6,1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.4",                      (CK_CHAR_PTR)"2.5.29.17",                                                           // Дополнительное имя (пример с кодированием в виде DER)                      (CK_CHAR_PTR)"DER:30:0F:81:0D:65:78:61:6d:70:6c:65:40:79:61:2E:72:75",                      (CK_CHAR_PTR)"2.5.29.32",                                                           // Политики сертификата (кодирование в виде DER с пометкой "critical")                      (CK_CHAR_PTR)"critical,DER:30:0A:30:08:06:06:2A:85:03:64:71:01",                      (CK_CHAR_PTR)"1.2.643.100.111",                                                     // Средства электронной подписи владельца                      (CK_CHAR_PTR)"ASN1:UTF8String:СКЗИ \\\"Рутокен ЭЦП 2.0\\\"", };    CK_BYTE_PTR csr;                                   // Указатель на буфер, содержащий подписанный запрос на сертификат    CK_ULONG csrSize;                                  // Размер запроса на сертификат в байтах    char* csrPem;                                      // Строка с CSR в формате PEM    CK_RV rv;    int errorCode = 1;    /*************************************************************************    * Создать запрос на сертификат                                           *    *************************************************************************/    printf("\nCreating CSR...\n");    /*************************************************************************    * Создание запроса на сертификат                                         *    *************************************************************************/    rv = functionListEx->C_EX_CreateCSR(session, publicKey, dn, arraysize(dn), &csr, &csrSize, privateKey, NULL_PTR, 0, exts, arraysize(exts));    CHECK_AND_LOG(" C_EX_CreateCSR", rv == CKR_OK, rvToStr(rv), exit);    /*************************************************************************    * Сконвертировать и распечатать буфер в формате PEM                      *    *************************************************************************/    GetCSRAsPEM(csr, csrSize, &csrPem);    CHECK(" Get CSR in PEM format", csrPem != NULL, free_csr);    printf("\nCertificate request is:\n");    printf("%s\n", csrPem);    errorCode = 0;    printf("Creating CSR has been completed successfully.\n");free_csr_pem:    free(csrPem);free_csr:    rv = functionListEx->C_EX_FreeBuffer(csr);    CHECK_RELEASE_AND_LOG(" C_EX_FreeBuffer", rv == CKR_OK, rvToStr(rv), errorCode);exit:    return errorCode;}

Можно заметить, что работа с функциями расширения очень похожа на работу со стандартными функциями. Основное отличие лишь в том, что мы обращаемся к другому списку функций CK_FUNCTION_LIST_EXTENDED_PTR. Создание запроса на сертификат происходит в одну строчку функцией C_EX_CreateCSR и возвращает запрос в DER-формате. Также стоит обратить внимание, что память, выделенную внутри библиотеки, следует высвобождать с помощью функции C_EX_FreeBuffer.

По полученному запросу можно получить сертификат в Удостоверяющем центре. Например, воспользуемся тестовым УЦ КриптоПро для получения сертификата. Полученный сертификат необходимо скачать в DER-формате, сохранить в файле с именем "cert_2012-256.cer" и положить в директорию, из которой вы запускаете примеры. Полученный сертификат можно импортировать на токен.

Импорт сертификата на токен. Создание объекта вручную

Сертификаты на токене так же как и ключи являются объектами. Сертификат можно импортировать на токен, создав объект с нужными атрибутами. Для этого мы реализовали функцию: import_cert:

int import_cert(CK_SESSION_HANDLE session){    CK_OBJECT_CLASS certificateObject = CKO_CERTIFICATE;    CK_BYTE keyPairIdGost2012_256[] = { "GOST R 34.10-2012 (256 bits) sample key pair ID (Aktiv Co.)" };    CK_BBOOL attributeTrue = CK_TRUE;    CK_BBOOL attributeFalse = CK_FALSE;    CK_CERTIFICATE_TYPE certificateType = CKC_X_509;    CK_ULONG tokenUserCertificate = 1;    /*************************************************************************    * Шаблон для импорта сертификата                                         *    *************************************************************************/    CK_ATTRIBUTE certificateTemplate[] =    {        { CKA_VALUE, 0, 0 },                                                               // Значение сертификата (заполняется в процессе работы)        { CKA_CLASS, &certificateObject, sizeof(certificateObject) },                      // Класс - сертификат        { CKA_ID, &keyPairIdGost2012_256, sizeof(keyPairIdGost2012_256) - 1 },             // Идентификатор сертификата (совпадает с идентификатором соответствующего ключа)        { CKA_TOKEN, &attributeTrue, sizeof(attributeTrue) },                              // Сертификат является объектом токена        { CKA_PRIVATE, &attributeFalse, sizeof(attributeFalse) },                          // Сертификат доступен без аутентификации        { CKA_CERTIFICATE_TYPE, &certificateType, sizeof(certificateType) },               // Тип сертификата - X.509        { CKA_CERTIFICATE_CATEGORY, &tokenUserCertificate, sizeof(tokenUserCertificate) }, // Категория сертификата - пользовательский    };    FILE* certFile;                                   // Поток ввода сертификата    CK_BYTE_PTR certDer;                              // Массив с сертификатом в DER формате    CK_ULONG certSize;                                // Размер массива сертификата    CK_OBJECT_HANDLE certificate;                     // Хэндл сертификата    CK_RV rv;    int r;    int errorCode = 1;                                // Флаг ошибки    /*************************************************************************    * Открыть поточный ввод сертификата из файла                             *    *************************************************************************/    certFile = fopen("cert_2012-256.cer", "rb");    CHECK_AND_LOG(" fopen", certFile != NULL, "\"cert_2012-256.cer\" doesn't exist", exit);    /*************************************************************************    * Определить размер файла, содержащего сертификат                        *    *************************************************************************/    r = fseek(certFile, 0, SEEK_END);    CHECK(" fseek", r == 0, close_certFile);    certSize = ftell(certFile);    CHECK(" ftell", certSize > 0, close_certFile);    r = fseek(certFile, 0, SEEK_SET);    CHECK(" fseek", r == 0, close_certFile);    /*************************************************************************    * Выделить память для сертификата                                        *    *************************************************************************/    certDer = (CK_BYTE_PTR)malloc(certSize);    CHECK(" malloc", certDer != NULL, close_certFile);    /*************************************************************************    * Прочитать сертификат                                                   *    *************************************************************************/    r = (int)fread(certDer, 1, (int)certSize, certFile);    CHECK(" fread", r == (int)certSize, free_certificate);    /*************************************************************************    * Задать шаблон сертификата для импорта                                  *    *************************************************************************/    certificateTemplate[0].pValue = certDer;    certificateTemplate[0].ulValueLen = certSize;    /*************************************************************************    * Создать сертификат на токене                                         *    *************************************************************************/    rv = functionList->C_CreateObject(session, certificateTemplate, arraysize(certificateTemplate), &certificate);    CHECK_AND_LOG(" C_CreateObject", rv == CKR_OK, rvToStr(rv), free_certificate);    errorCode = 0;    printf("Certificate has been created successfully\n");    /*************************************************************************    * Очистить память из-под строки с сертификатом                           *    *************************************************************************/free_certificate:    free(certDer);    /*************************************************************************    * Закрыть поток ввода сертификата                                        *    *************************************************************************/close_certFile:    r = fclose(certFile);    CHECK_RELEASE(" fclose", r == 0, errorCode);exit:    return errorCode;}

На этот раз мы создали объект напрямую с помощью функции C_CreateObject. Для создания объекта сертификата мы аналогично передали шаблон с атрибутами объекта: тело сертификата, идентификатор, тип доступа, тип сертификата и т.п.

Функцию C_CreateObject можно использовать не только для создания сертификата на токене, но и для создания других объектов, например, публичных ключей (CKO_PUBLIC_KEY), закрытых ключей (CKO_PRIVATE_KEY), симметричных ключей (CKO_SECRET_KEY), обычных данных (CKO_DATA). Их значение будет также содержаться внутри атрибута CKA_VALUE.

Теперь, когда у нас на токене имеется и ключевая пара и сертификат, для закрепления материала рассмотрим операцию создания CMS-подписи.

Формирование CMS-подписи

Данная возможность является расширением библиотеки Рутокен и может работать только с ГОСТ-ключами. Для создания подписи в формате CMS требуется наличие закрытого ключа и сертификата (неявно содержащего в себе открытый ключ). Создание CMS-подписи реализовано в функции sign_cms:

int sign_cms(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE certificate, CK_OBJECT_HANDLE privateKey){    /*************************************************************************    * Данные для подписи                                                     *    *************************************************************************/    CK_BYTE data[] =    {        0x01, 0x00, 0x02, 0x35, 0x35,        0x02, 0x00, 0x01, 0x01,        0x81, 0x00, 0x09, 0x34, 0x30, 0x34, 0x34, 0x34, 0x35, 0x39, 0x39, 0x38,        0x82, 0x00, 0x0A, 0x37, 0x37, 0x38, 0x31, 0x35, 0x36, 0x34, 0x36, 0x31, 0x31,        0x83, 0x00, 0x13, 0x41, 0x6B, 0x74, 0x69, 0x76, 0x20, 0x52, 0x75, 0x74, 0x6F, 0x6B, 0x65, 0x6E, 0x20, 0x42, 0x61, 0x6E, 0x6B, 0x2E,        0x84, 0x00, 0x14, 0x34, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x37, 0x36,        0x85, 0x00, 0x0A, 0x33, 0x32, 0x32, 0x38, 0x37, 0x33, 0x36, 0x37, 0x36, 0x35,        0x86, 0x00, 0x03, 0x52, 0x55, 0x42,        0xFF, 0x00, 0x0D, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30    };    CK_BYTE_PTR signature;                             // Указатель на буфер, содержащий подпись исходных данных    CK_ULONG signatureSize;                            // Размер буфера, содержащего подпись исходных данных, в байтах    char* signaturePem;                                // Строка с CMS в формате PEM    CK_RV rv;    int errorCode = 1;                                 // Флаг ошибки    /*************************************************************************    * Подписать данные                                                       *    *************************************************************************/    rv = functionListEx->C_EX_PKCS7Sign(session, data, sizeof(data), certificate,        &signature, &signatureSize, privateKey, NULL_PTR, 0, USE_HARDWARE_HASH);    CHECK_AND_LOG(" C_EX_PKCS7Sign", rv == CKR_OK, rvToStr(rv), exit);        /*************************************************************************        * Сконвертировать и распечатать буфер в формате PEM                      *        *************************************************************************/        GetCMSAsPEM(signature, signatureSize, &signaturePem);        CHECK(" Get CMS in PEM format", signaturePem != NULL, free_signature);        printf("\nSignature is:\n");        printf("%s\n", signaturePem);    errorCode = 0;    printf("Data has been signed successfully.\n");free_signature_pem:    free(signaturePem);    /*************************************************************************    * Освободить память, выделенную в библиотеке                             *    *************************************************************************/free_signature:    rv = functionListEx->C_EX_FreeBuffer(signature);    CHECK_RELEASE_AND_LOG(" C_EX_FreeBuffer", rv == CKR_OK, rvToStr(rv), errorCode);exit:    return errorCode;}

Создание CMS-подписи произошло вызовом всего лишь одной функции расширения C_EX_PKCS7Sign. А объект сертификата нашелся так же просто, как и объект ключа с минимальными отличиями в коде. Все это показывает, как просто и лаконично (по меркам языка C) спроектирован стандарт PKCS#11 с идеей объектного подхода.

Получение и установка атрибутов публичных объектов

В завершение главы про объекты хотелось бы отметить, что атрибуты публичных объектов можно получать и изменять. Делается это с помощью функций C_SetAttributeValue и C_GetAttributeValue.

Логика работы с функцией C_SetAttributeValue очень похожа на логику создания объектов создаётся шаблон, атрибуты заполняются указанными значениями. Все это передаётся на вход функции C_SetAttributeValue. Изменять атрибуты можно, если у объекта атрибут CKA_MODIFIABLE равен CK_TRUE.

При получении значений атрибутов иногда неизвестно, какой будет выходной размер атрибута. В таком случае создается шаблон с нулевыми значениями указателей на выходные объекта атрибутов и их размеров. Этот шаблон передаётся функции C_GetAttributeValue. Функция заполняет значение выходных размеров атрибутов в этом шаблоне. Мы может воспользоваться этой информацией для выделения памяти под атрибуты в шаблоне и дальнейшего вызова функции C_GetAttributeValue.

Для демонстрации работы функций C_GetAttributeValue и C_SetAttributeValue рассмотрим пример получения тела сертификата и изменения текстовой метки сертификата:

int get_cert(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE cert){    CK_BYTE_PTR body = NULL_PTR;    CK_ATTRIBUTE template[] = {        {CKA_VALUE, NULL_PTR, 0}    };    char* certPem;    CK_RV rv;    int errorCode=1;        /*************************************************************************        * Получение размера тела сертификата                                     *        *************************************************************************/    rv = functionList->C_GetAttributeValue(session, cert, template, arraysize(template));    CHECK_AND_LOG(" C_GetAttributeValue", rv == CKR_OK, rvToStr(rv), exit);    body = (CK_BYTE_PTR) malloc(template[0].ulValueLen);    template[0].pValue = body;        /*************************************************************************        * Получение тела сертификата в формате DER                               *        *************************************************************************/    rv = functionList->C_GetAttributeValue(session, cert, template, arraysize(template));    CHECK_AND_LOG(" C_GetAttributeValue", rv == CKR_OK, rvToStr(rv), free);        /*************************************************************************        * Сконвертировать и распечатать буфер в формате PEM                      *        *************************************************************************/        GetCertAsPem(body, template[0].ulValueLen, &certPem);        CHECK(" Get cert in PEM format", certPem != NULL, free);        printf("\nCertificate request is:\n");        printf("%s\n", certPem);    errorCode = 0;        printf("Getting cert body has been completed successfully.\n");free_cert_pem:        free(certPem);free:    free(body);exit:    return errorCode;}int set_cert_label(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE cert){    CK_UTF8CHAR label[] = {"GOST certificate"};    CK_ATTRIBUTE template[] = {        CKA_LABEL, label, sizeof(label)-1    };    CK_RV rv;    int errorCode = 1;        /*************************************************************************        * Установка метки сертификата                                            *        *************************************************************************/    rv = functionList->C_SetAttributeValue(session, cert, template, arraysize(template));    CHECK_AND_LOG(" C_SetAttributeValue", rv == CKR_OK, rvToStr(rv), exit);    errorCode = 0;exit:    return errorCode;}

Про механизмы

Мы уже ранее встречались с механизмами в примерах и дали краткое описание. Давайте теперь опишем их подробнее. Описание механизмов в PKCS#11 было вынесено в отдельный документ, с которым можно ознакомиться здесь. В этом документе написано:

A mechanism specifies precisely how a certain cryptographic process is to be performed. PKCS #11 implementations MAY use one of more mechanisms defined in this docuоment.

Отсюда следует, что механизмы:

  • Определяют некоторое криптографическое преобразование.

  • PCKS#11 может использовать механизмы, определенные в этом документе.

Более того, некоторые PKCS#11-библиотеки могут использовать и другие механизмы.

Согласно документации механизмы можно разделить на:

  • Механизмы шифрования и расшифрования (Encrypt & Decrypt);

  • Механизмы подписи и проверки подписи (Sign & Verify);

  • Механизм формирования хеша (Digest);

  • Механизм восстановления подписи по публичному ключу (Sign Recover & Verify Recovery);

  • Механизм генерации симметричных и асимметричных ключей (Gen. Key/Key Pair);

  • Экспорт и импорт ключей (Wrap & Unwrap);

  • Выработка общего ключа на основе асимметричных ключей (Derive).

Каждый механизм идентифицирует одну или несколько из этих функций. Так, например, ранее рассмотренный механизм CKM_GOSTR3410_KEY_PAIR_GEN предназначен для генерации ключей, а механизм CKM_AES_ECB может использоваться как для зашифрования/расшифрования, так и для свертки/развертки ключей.

Работа с механизмами, на примере зашифрования сообщения

Механизмы в PKCS#11 задаются через структур CK_MECHANISM. Объекты типа CK_MECHANISM в дальнейшем передаются PKCS#11-функциям для указания нужного механизма. Сама структура CK_MECHANISM состоит из трех элементов:

  1. Идентификатор механизма (mechanism);

  2. Указатель на параметры механизма (pParameter);

  3. Длина в байтах параметров механизма (ulParameterLen).

Самый простой пример параметра механизма вектор инициализации для алгоритмов шифрования. Попробуем на примере показать, как можно зашифровать сообщение через механизм CKM_GOST28147 с указанным вектором инициализации. Пример реализован внутри функции encrypt_data:

int encrypt_data(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE secretKey){    /* Имитовставка */    CK_BYTE iv[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f };    /*  Механизм программного шифрования/расшифрования по алгоритму ГОСТ 28147-89 */    CK_MECHANISM gost28147EncDecMech = {CKM_GOST28147, iv, sizeof(iv)};    /*************************************************************************    * Данные для шифрования                                                  *    *************************************************************************/    CK_BYTE data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,                   0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,                   0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,                   0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00 };    CK_BYTE_PTR encrypted;                            // Указатель на временный буфер для зашифрованных данных    CK_ULONG encryptedSize;                           // Размер временного буфера в байтах    CK_RV rv;    int errorCode = 1;    /*************************************************************************    * Инициализировать операцию шифрования                                   *    *************************************************************************/    rv = functionList->C_EncryptInit(session, &gost28147EncDecMech, secretKey);    CHECK_AND_LOG(" C_EncryptInit", rv == CKR_OK, rvToStr(rv), exit);    /*************************************************************************    * Зашифровать данные (при шифровании с использованием механизма          *    * CKM_GOST28147_ECB размер данных должен быть кратен 8)                  *    *************************************************************************/    encryptedSize = sizeof(data);    encrypted = (CK_BYTE_PTR)malloc(encryptedSize * sizeof(CK_BYTE));    CHECK("  Memory allocation for encrypted data", encrypted != NULL_PTR, exit);    rv = functionList->C_Encrypt(session, data, sizeof(data), encrypted, &encryptedSize);    CHECK_AND_LOG(" C_Encrypt", rv == CKR_OK, rvToStr(rv), free_encrypted);    /*************************************************************************    * Распечатать буфер, содержащий зашифрованные данные                     *    *************************************************************************/    printf(" Encrypted buffer is:\n");    printHex(encrypted, encryptedSize);    printf("Encryption has been completed successfully.\n");    errorCode = 0;free_encrypted:    free(encrypted);exit:    return errorCode;}

В этом примере стоит обратить внимание на то, как передаётся вектор инициализации механизму шифрования. Также стоит заметить, что после вызова функции C_Encrypt вызывать функцию C_EncryptFinish не нужно, т.к. C_Encrypt внутри себя вызывает функцию C_EncryptUpdate, а после неё уже завершает процесс шифрования с помощью функции C_EncryptFinish.

Проверка поддержки механизмов

Ранее в примерах мы всегда надеялись на то, что токен поддерживает используемые механизмы, но, вообще говоря, это может быть не так. Так, например, Рутокен Lite не поддерживает криптографические механизмы. Поэтому перед началом работы с каким-либо механизмом желательно убедиться в том, что он поддерживается устройством. Это можно сделать с помощью функции C_GetMechanismList, которая возвращает список поддерживаемых механизмов токена. Напишем удобную обертку для проверки поддержки определенного механизма:

int mech_supports(CK_SLOT_ID slot, CK_MECHANISM_TYPE mech, int* mechIsSupported){    CK_MECHANISM_TYPE_PTR mechanisms;                 // Массив поддерживаемых механизмов    CK_ULONG mechanismCount;                          // Количество поддерживаемых механизмов    CK_RV rv;    int errorCode = 1;    /*************************************************************************    * Получить список поддерживаемых токеном механизмов                      *    *************************************************************************/    rv = functionList->C_GetMechanismList(slot, NULL_PTR, &mechanismCount);    CHECK_AND_LOG(" C_GetMechanismList (number of mechanisms)", rv == CKR_OK, rvToStr(rv), exit);    CHECK_AND_LOG(" Checking mechanisms available", mechanismCount > 0, " No mechanisms available", exit);    mechanisms = (CK_MECHANISM_TYPE_PTR)malloc(mechanismCount * sizeof(CK_MECHANISM_TYPE));    CHECK(" Memory allocation for mechanisms", mechanisms != NULL_PTR, exit);    rv = functionList->C_GetMechanismList(slot, mechanisms, &mechanismCount);    CHECK_AND_LOG(" C_GetMechanismList", rv == CKR_OK, rvToStr(rv), free_mechanisms);    /*************************************************************************    * Определение поддерживаемых токеном механизмов                          *    *************************************************************************/    for (size_t i = 0; i < mechanismCount; ++i) {        if (mechanisms[i] == mech) {            *mechIsSupported = 1;            break;        }    }    errorCode = 0;    if (*mechIsSupported)        printf("Mechanism is supported\n");    else        printf("Mechanism is not supported\n");free_mechanisms:    free(mechanisms);exit:}

Эта обертка удобна для одноразовой проверки поддержки механизма. Если же мы хотим проверить поддержку сразу нескольких механизмов, то лучше получить список поддерживаемых механизмов токена один раз и проверить наличие требуемых механизмы. Это позволит сократить количество обращений к токену и время работы программы, соответственно.

Утилита pkcs11-tool

Часто бывает необходимо просто и быстро выполнить какое-либо обращение к PKCS#11-библиотеке, не прибегая к написанию кода. На помощь может прийти утилита pkcs11-tool, которая распространяется в составе пакета (opensc)[https://github.com/OpenSC/OpenSC].

Утилита pkcs11-tool может гибко выполнять огромное количество стандартных PKCS#11-операций. Например:

  • Генерация ключевых пар:
    pkcs11-tool --module /usr/lib/librtpkcs11ecp.so --keypairgen --key-type GOSTR3410-2012-256:B --id 45 -l

  • Создание сырой подписи:
    pkcs11-tool --module /usr/lib/librtpkcs11ecp.so --sign --id 45 -l -i file.txt --mechanism GOSTR3410-WITH-GOSTR3411-12-256

  • Генерация псевдослучайных последовательностей:
    pkcs11-tool --module /usr/lib/librtpkcs11ecp.so --generate-random 16

  • Получение списка объектов на токене:
    pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -O

  • Получение механизмов, поддерживаемых токеном:
    pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -M

Список не является исчерпывающим, более подробно о полных возможностях утилиты можно прочитать в мануале.

Дополнительный материал

Если вы захотите узнать, как работать с более специфичными функциями, то большое количество примеров по работе с PKCS#11 можно найти в нашем SDK. Все примеры по работе с PKCS#11-библиотекой находятся в директории sdk\pkcs11\samples.

Выводы

Зачастую библиотека PKCS#11 является основным кирпичиком при написании приложений или других библиотек для работы со смарт-картами на других языках программирования. Поэтому знание основ работы с PKCS#11 на языке C может помочь разобраться с тем, как работать с обертками или как лучше реализовать архитектуру новой обертки.

Кроме PKCS#11 с объектами на смарт-картах/токенах можно работать через:

Если вы хотите узнать про какую-то реализацию подробнее, то напишите в комментариях и мы постараемся в ближайшее время выпустить новую статью с её разбором.

Подробнее..

Интернет вещей по-русски. Безопасность в OpenUNB

08.03.2021 14:16:44 | Автор: admin

В настоящее время в эпоху развитого интернета мы настолько привыкли к хорошей информационной безопасности протоколов передачи информации, что тема создания новых протоколов несколько ушла в тень. Зачем что-то еще изобретать? Просто выбери из имеющихся. Но Интернет вещей поднимает эту тему заново.


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


Различных радио-протоколов Интернета вещей сейчас наплодилось огромное множество, но в сфере LPWAN не везде разработчики протокола передачи берут на себя тяжелую ношу защиты информации. Можно долго спорить, нужно ли вообще включать защиту в радио-протокол LPWAN, а не перекладывать ее на вышележащие уровни, но если предложенная схема хороша, то стоит надеяться на большую вероятность внедрения стандарта в индустрию. Главный тезис: вообще без защиты в Интернете вещей сейчас уже нельзя. И она должна быть на уровне.


Так же рассуждали и разработчики OpenUNB, ставя во главу угла энергоэффективность и встроенную безопасность информации. Тем, кто еще ничего не знает про OpenUNB, я советую прочитать предыдущие статьи на эту тему:



а также изучить первоисточник на сайте Сколтеха.


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


Чтобы обеспечить базу безопасности, у каждого устройства и сервера должно быть что-то общее, секретное, которое никто не знает. В OpenUNB это секретный ключ шифрования K0, имеющий длину k, равную 128 или 256 бит. Весь остальной протокол должен быть сделан так, чтобы этот базовый, основной ключ не стал бы известен никому никаким образом, а информация при этом была защищена. Поэтому базовый ключ претерпевает такое множество преобразований в своем стремлении встретиться с информацией.


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


В недрах устройства и сервера OpenUNB есть счетчик активаций Na и счетчик эпох Ne, которые остаются примерно одинаковыми в процессе работы системы. Сначала вырабатывается ключ активации:


$ Ka = CTR (K0, Na || 0^{n/2-16}, 0^k).$


Потом уже по ключу активации вырабатываются рабочие ключ шифрования Km и ключ имитозащиты Ke:


$ Km = CTR (Ka, 0x02 || Ne || 0^{n/2-32}, 0^k),$


$ Ke = CTR (Ka, 0x03 || Ne || 0^{n/2-32}, 0^k).$


Для криптографических преобразований разработчики OpenUNB рекомендуется использовать блочный шифр Магма, который имеет длину блока n = 64 бита и длину ключа k = 256 бит, определенный в стандарте ГОСТ Р 34.12-2015.


Шифрование выглядит так:


$ EncryptedMACPayload = CTR (Ke, Nn || 0^{n/2-16}, MACPayload),$


где Nn номер пакета, а выработка имитовставки так:


$MIC = CMAC24 (Km, P),$


где значение вектора P зависит от длины блока n и длины поля MACPayload. Пусть len
однобайтная целочисленная беззнаковая переменная, равная длине полезных данных
MACPayload в битах (переменная len может принимать значения 16 или 48), тогда:


если len = 48 и n = 64, то


$P = DevAddr || MACPayload || Nn || 0^{2n-len-48} || len;$


в остальных случаях


$P = DevAddr || MACPayload || Nn || 0^{n-len-48} || len.$


Процедура CMAC24 здесь это криптопреобразование "режим выработки имитовставки" согласно ГОСТ.


Далее эти поля вставляются на свои места. Напомним схему формирования пакета:
image


Более подробно, со всеми деталями, вы можете прочитать о безопасности в OpenUNB в первоисточнике.


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


Как всегда, исходники крипто-преобразований от deef137 доступны на Github.

Подробнее..

IETF официально прекратил поддержку протоколов TLS 1.0 и 1.1

25.03.2021 00:06:26 | Автор: admin

CC-BY-CA Vadim Rybalko, на основе мема

Рабочая группа инженеров Интернета IETF признала устаревшими протоколы шифрования TLS 1.0 и 1.1. Соответствующие стандарты RFC официально получили исторический статус с пометкой deprecated.

Пометка deprecated означает, что IETF настоятельно не рекомендует использовать эти протоколы. В целях безопасности требуется отключить поддержку TLS 1.0 и 1.1 везде, где это возможно. Об этом говорится в опубликованном RFC 8996. Почему нельзя поддерживать протоколы TLS 1.0 и 1.1 подробно объясняется в пунктах 3, 4 и 5 этого документа.



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

Одновременно со старыми версиями TLS также признаётся устаревшим протокол Datagram TLS (DTLS) версии 1.0 (RFC 4347), и только он, поскольку версия 1.1 не выходила.

Стандарту TLS 1.0 в этом году исполнилось 22 года. С момента его принятия стало лучше понятно, как следует проектировать протоколы шифрования. Выросли требования к надёжности шифров. К сожалению, TLS 1.0 и 1.1 не соответствуют этим требованиям.

Самое плохое, что TLS 1.0 и 1.1 не поддерживают работу с современными криптографическими шифрами. Например, при рукопожатии в них обязательно применяется алгоритм хэширования SHA-1. В этих версиях TLS невозможно установить более сильный алгоритм хэширования для подписей ServerKeyExchange или CertificateVerify.

Черновик данного RFC 8996 был опубликован 14 сентября 2018 года. В числе прочего, там упоминается, что алгоритм SHA-1 с криптостойкостью 2^77 нельзя считать безопасным по современным меркам: 2^77 операций [для атаки] это ниже допустимой границы безопасности.



Речь идёт об атаке BEAST (Browser Exploit Against SSL/TLS) на TLS 1.0 и 1.1, а точнее на блочные шифры, где в качестве вектора инициализации для сообщения n используется последний блок шифрования предыдущего сообщения (n-1).

Разработчики всех ведущих браузеров сразу согласились выполнить рекомендации IETF.

Браузер Chrome первым отказался от поддержки старых версий TLS в январе 2019 года. Начиная версии 79 для устаревших протоколов выводилось предупреждение в консоли DevTools, а полное отключение запланировали на версию Chrome 81 в марте 2020 года (предварительные версии с января 2020 года). Одновременно отказ от TLS 1.0 и 1.1 анонсировали Microsoft, Mozilla и Apple.

Правда, всё пошло не по плану. В марте 2020 года Firefox временно отказался удалять поддержку TLS 1.0 и 1.1. Формально это было сделано из-за коронавируса (см. скриншот ниже), но фактически разработчики Mozilla побоялись, что коллеги из Google пойдут на попятную и оставят поддержку TLS 1.0 и 1.1, так что Firefox станет единственным браузером без этой поддержки.



Но в итоге поддержку старых протоколов в браузерах всё-таки отключили. В случае необходимости в Firefox можно изменить настройку security.tls.version.enable-deprecated.

Постепенно TLS 1.0 и 1.1 удаляют из приложений и сервисов. Это сделали Amazon, CloudFlare, GitHub, KeyCDN, PayPal и другие веб-сервисы. С 15 января 2020 года поддержка старых протоколов отключена на ресурсах Хабра.
Подробнее..

Криптофронт Второй Мировой Войны часть 1

30.03.2021 10:05:43 | Автор: admin

Автор: Forbidden

Еще одна статья про взломЭнигмы?! Конечно нет, мы поговорим обо ВСЕХ шифровальных машинах, активно использовавшихся во Второй Мировой Войне, и, конечно же, поговорим о том, как их пытались взломать.

Радиоперехват стал известен с Первой мировой войны, поэтому ко второй мировой все страны-участники подготовились основательно, поэтому и бои на криптофронте были не менее ожесточенными.

Япония, СССР, Великобритания, США и Германия, в этой статье рассмотрим атаку и защиту каждой страны, ответим и на нестандартные вопросы:

  • Почему шифровальщики носили с собой взрывчатку?

  • Почему в армии США ценили коренных американцев?

  • Как сводки погоды, минные поля и нацистские приветствия помогали союзникам?

  • Почему СССР даже не пытался взломать шифровальные машины Германии?

  • И почему математически идеальный шифр все равно расшифровывали?

Интересно? Тогда добро пожаловать под кат!

Немного об истории криптографии

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

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

В 1912 году был придуман идеальный шифр Шифр Вернама. Позднее в 1945 году американский инженерКлод Шеннонв своей работе Математическая теория криптографии доказал абсолютную стойкость данного шифра, его работу рассекретили лишь после второй мировой войны в 1949 году. С точки зрения математики и криптографии это действительно идеальный шифр и по сей день, но вот его реализация была довольно сложной задачей.

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

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

Эта тенденция сформировалась по двум основным причинам:

  • Захват шифровальной машины значительно облегчал ее взлом.

  • Надежность машины коррелировала с ее размером.

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

А теперь немного конкретики по странам.

Япония

Защита

Японская Армия и Флот независимо друг от друга разрабатывали свои шифровальные машины.

С 1930-1941 год Японская Армия разработала 3 шифровальные машины, которые не уцелели после войны, (92-shiki injiki,97-shiki injikiи1-shiki 1-go injiki), принцип их работы был аналогичен немецкой Энигме, но использовались они редко. О случаях взлома этих машин не известно.Не взломаны.

Казалось бы, Японцы молодцы? Но нет, руководство страны больше доверяло разработкам Военно-Морского флота, а руководить ими поставили совсем не того человека, математикаТэйдзи Такаги, не имевшего опыта в криптоанализе.

Шифровальные машины Печатная машина Тип-91, которые впоследствии получат у криптоаналитиков США название Red ( красный), были поставлены в МИД еще до войны в 1931 году. Руководство страны считало машиныRedболее защищенными, чем армейские машины, а это было не так.

Позднее в 1938 году они будут модернизированы до машин Алфавитная печатная машина типа 97, получившим в США название Purple (пурпурный).
Эта машина действительно была гораздо надежнее, чем Red, но руководству Флота не было известно, что Red уже взломан, а основная ее уязвимость при модернизации никуда не делась и была перенесена вPurple. В итоге обе машины...Взломаны(СССР, США, Великобритания).

Макет Purple в музее СШАМакет Purple в музее СШААрхивное фото рабочего места с PurpleАрхивное фото рабочего места с Purple

В чем же заключались основные ошибки японцев при проектировании и использованииRedиPurple?

  • Основной уязвимостью данного типа машин оказалось то, что шифровали они 20 согласных и 6 гласных буквы по отдельности, как две группы. Получалось так, что это были практически независимые шифры, взламывать которые можно было по отдельности и гораздо менее стойкие ко взлому, чем единый шифр. Особенно легко взламывались гласные (их всего 6), а по их положению угадать слова целиком было уже не сложно. Эта атака получила названиеатака 6-20 - 6-20 attack.

  • Было легко предсказать изменения ключа. Для каждых десяти дней месяца они генерировали ключ заново, но потом только немного изменяли каждый день. То есть взломав ключ одного дня, можно было легко предсказать, каким он будет до конца декады, или каким он был ранее в этой декаде.

Непосредственно для себя Военно-Морским Флотом были разработаны шифровальные машины:

  • Orange(оранжевый) печатная машина типа 91 для аташе Военно-морского флота, аналогичнаяRed,взломана(США, СССР).

  • Jade(нефритовый) Печатная машина типа 97, одна из самых защищенных шифровальных машин Японии. Использовалась на флоте. Шифровала и заглавные и строчные буквы, что уменьшало стойкость шифра.Взломана в большинстве случаев(США).

  • Coral(коралловый) печатная машина типа 97 версия 3 самая защищенная шифровальная машина Японии, преемникJade. Использовалась на флоте.Взломана в большинстве случаев(США).

  • Green(зеленый) шифровальная машина, внедрение которой было запланировано, но так и не было осуществлено до войны.

JADEв музее СШАJADEв музее США

Как видно,JadeиCoralне были полностью взломаны даже после захвата, это все потому, что флотские инженеры перестали использовать замечательные собственные разработки. При разработкеJadeиCoralони основывались на строенииЭнигмы, шифровальной машине Германии, что крайне благоприятно сказалось на стойкости. О том, как именно их взламывали я рассказу в разделе Атака о США.

Достоверно известно, что информацию о взломеPurpleФлоту Японии неоднократно передавали из Германии, об этом сообщало и высшее руководство Японской Армии. Да быть такого не может, шифр абсолютно надежен, ищите шпионов. примерно такой был ответ.. До конца войны принцип работыPurpleне будет изменен и переписка МИДа Японии останется открытой для СССР и США.

Цена фанатичной самоуверенности была высока. Например, сведения о подготовке атаки на Перл-Харбор, направленные в посольство Японии в Вашингтоне, были перехвачены и расшифрованы, не успели только доложить наверх. После начала войны, этот факт всплыл в ходе разбирательств о непредотвращении в Конгрессе США, что недвусмысленно намекало Японцам на то, чтоPurpleбыл взломан, но выводы сделаны не были.

Японский посолХироси Осимаочень внимательно изучал оборону своих союзников - Германии. Например, он доложил, используяPurple, своему командованию о расположении укреплений атлантического вала. Сообщение было перехвачено союзниками, расшифровано и очень помогло при высадке в Нормандии.

ВзломRedпозволил получить информацию о Берлинском пакте.

В 21 веке криптографию можно изучать даже по манге, выводы сделаны?)В 21 веке криптографию можно изучать даже по манге, выводы сделаны?)

Что же, японцы могли в атаке на криптографическом фронте?

А вот достоверных сведений о проводимых Японцами попытках взлома шифров США или СССР, к сожалению нет.

СССР

Защита

Российская Империя была одним из лидеров в области криптографии в начале первой мировой войны. Однако, ввиду известных проблем начала 20-го века, советское государство несколько отстало в этой области от европейских стран и США.

До войны наиболее активно использовались импортные электромеханические шифровальные машины Франции B-211. Они использовались в РККА с 1939 года для замены ручного кодирования документов на оперативно-тактическом уровне.

Через год эти машины были модернизированы уже советскими инженерами и получили названиеК-37 Кристалл, весом всего 19кг (это мало для того времени). Однако, уже в 1941 на фоне отступления советской армии первый образецК-37был захвачен немецкой армией и передан на изучение. Немецкие криптографы отметили, что это устройство довольно простое и недостаточно криптостойкое, поэтому без труда быловзломано(Германия). Советское руководство, узнав о захвате машины, прекратило ее использование на западном фронте, в дальнейшем она использовалась лишь на Дальнем Востоке до 1945 г, а в 1947г и вовсе была снята с вооружения. Выводы были сделаны.

К-37 Кристаллархивное фотоК-37 Кристаллархивное фото

До войны, помимо модернизации B-211, СССР успешно вело разработки собственных шифровальных машин, особый вклад в это сделалИван Павлович Волосок- начальник 2 отделения 8 отдела штаба РККА. Именно он стал главным конструктором первой серийной шифровальной машины В-4, которая была выпущена в 1934 году. За что и получил Сталинскую премию.

Массовое производство было налажено в 1939, однако уже в 1940 не менее талантливый инженерНиколай Михайлович Шарыгинпровел серьезную модернизацию этой машины. Модернизированная версия получила названиеМ-100 Спектр.

Вступил во Вторую Мировую Войну СССР уже сМ-100, использовалась эта машина до 1942 года, до последующей модернизации. Стоит отметить,М-100весила порядка 141кг по сумме всех трех узлов, поэтому перемещали ее на специальных автобусах. Модернизированная же версия, получившая названиеМ-101 Изумрудуменьшился в размерах в 6 раз и по весу до 64,5 кг.М-101использовалась до конца войны, в том числе и на дальней бомбардировочной авиации.

М-101 Изумрудархивное фотоМ-101 Изумрудархивное фото

Всего во время второй мировой войны СССР использовало порядка 246 шифровальных машин: 150 К-37остальныеМ-100иМ-101. В их обслуживании было задействовано не менее 1857 человек личного состава, хотя по некоторым оценкам было обучено не менее 5000 специалистов-шифровальщиков.

В ходе войны,М-100иМ-101ни разу не были захвачены немецкой армией, добиться этого позволили следующие меры:

  • Крайне ответственное отношение шифровальной службы к своей работе, в документах РККА упоминаются факты героического уничтожения документов и шифровальной техники операторами, которыми приходилось работать в тяжелейших условиях.

  • Разумное использование техники с минимальной угрозой ее захвата.

М-100иМ-101не были взломаныв ходе войны.

Однако, после войны в 1946 году, по документам спецслужб США, возможно получивших образец машины, шифрМ-101был ими взломан в рамках начинающейся Холодной войны.

Атака

У РККА были свои группы дешифровальщиков, группыБориса АронскогоиСергея Толстого, которые работали над дешифровкой японских шифровRedиPurple. Подробных сведений об их работе нет, но судя по всему они использовали атаку 6-20 и удачно расшифровали сообщения японцев. Есть сведения, что эта группа подтвердила информацию от внедренного разведчика Зорге о том, что Япония не намерена в ближайшее время нападать на СССР. Основанное на этой информации решение руководства СССР о переброске подразделений с дальнего востока под Москву сложно переоценить. Но эти группы не дешифровали всю переписку МИДа Японии, им это просто напросто не было нужно, и вот почему.

В атаке на криптографическом фронте у СССР была особая тактика, которая сформировалась в недрах внешней разведки.

В 1927 году в Москве 38 летний сотрудник японского посольстваИдзуми Кодзоженился наЕлене Перской- дочери своей арендодательницы и генеральской вдовы. Намечалась вербовка, но все пошло не так, как планировалось.
Идзумивскоре перевели в посольство в Харбине, его жена и ребенок уехали вместе с ним, а его теща получила 10 лет лагерей за шпионаж.

Но вдруг в 1937 годуЕленавышла на связь с родиной, придя в консульский отдел в Праге полпредства с заявлением о том, что она планирует восстановить советское гражданство и воспитывать сына на родине. Отказать ей не могли, ведьИдзумина тот момент занял должность 3-го секретаря посольства в Праге и отвечал за шифрование. Вербовка началась уже заново, черезЕлену, через нее и планировали получить доступ к Японским шифрам, с отъездом в Москву сказали повременить.

С 1938 до 1940, покаИдзумиработал в Праге, он через жену передавал советской разведке расшифрованные доклады и сообщения.
Позднее, в 1940 году, его перевели в посольство в Софии, и уже там его деятельность принесла самые ценные плоды, несмотря на то, что его жену с ребенком все таки переправили в СССР.

Идзумипередавал блокноты с запасными и текущими ключами шифрования, едиными для всех европейских посольств Японии в СССР, передавал фактически после каждого их обновления. Да, это были не все ключи МИДа Японии, но солидная их часть. Также он и передавал документы/доклады, но ключи были ценнее всего.

Во время начала войны Германии против СССР были одномоментно заменены все ключи шифрования во всех посольствах Японии, новые ключи были выданы самым лояльным и проверенным сотрудникам посольств, 5 августа 1941 года эти ключи выдали иИдзуми, конечно даже они сразу были перенаправлены в СССР.

ДобытыеИдзумив начале войны ключи шифрования действовали вплоть до 1943 года, позволяя группамАронскогоиТолстогососредоточить свои усилия на других шифровках. В 1944-45 года связь с агентом исчезла, однако в 1946 она была восстановлена, и агент продолжил работу, но уже как частное лицо в своей новоорганизованной торговой фирме.

А что со взломом шифровальных машин Германии?

Никто в СССР серьезно этим не занимался, ведь у советской разведки был доступ к лучшим достижениям в этой области в мире к Блетчли-парку. Свой агент среди элиты Британской дешифровальной службы, один из знаменитойКембриджской Пятерки-Джон Кернкросс.

СоставКембриджской Пятеркиагентов впечатляет:

  • Ким Филби (Kim Philby) занимал высокие посты в SIS (MI6) и MI5.

  • Дональд Маклин (Donald Duart Maclean) работал в министерстве иностранных дел.

  • Энтони Блант (Anthony Blunt) контрразведка, советник короля Георга VI.

  • Гай Бёрджесс (Guy Burgess) контрразведка, министерство иностранных дел.

  • Джон Кернкросс (John Cairncross) министерство иностранных дел, военная разведка, шифровальщик.

Кембриджская Пятерка.О них стоит написать отдельный лонг)Кембриджская Пятерка.О них стоит написать отдельный лонг)

Джон Кернкросспередавал советской разведке все расшифрованные британцами сообщения, которые СССР не передавали как союзнику по официальным каналам.

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

Немного дешевле, чем Британская секретная программа, правда?

В следующей части

В следующей части я расскажу уже про Великобританию, США и Германию.
Каждая из этих стран пошла на автоматизацию криптоанализа, на создание первых вычислительных машин.

  • Кому это удалось? Ответ вас очень удивит)

  • Почему вера в невзламываемуюЭнигмуподвела немцев дважды?

  • Какие были нестандартные методы шифрования, и причем тут коренные американцы?

  • Не обойдемся, конечно, без самого взломаЭнигмыи машиныЛоренца, но с интересными подробностями)

Автор: Forbidden

Оригинал

Подробнее..

Криптофронт Второй Мировой Войны, часть 2

31.03.2021 10:06:06 | Автор: admin

Автор: Forbidden World

Думаете, что и так знаете про успехи США, Германии и особенно Великобритании в области криптографии?

Я же смотрел Игру в имитацию! - распространенный комментарий, распространенное заблуждение. Все было не так, как показали в кино. Британцы были не самые умные, Алан Тьюринг не был самым успешным в дешифровальной службе, а США не полагались только на своих союзников. Ну и британский компьютер, который вы видите ниже на картинке, НИКАК не связан с Тьюрингом.

А материал об успехе дешифровальной службы Германии впервые переведен на русский язык для этой статьи. Пришло время срывать покровы, интересно узнать как оно было на самом деле?

Часть 1

Добро пожаловать под кат!

Non Turing Complete machine.Non Turing Complete machine.

Великобритания

Защита

Британия одна из первых вступила в войну, и ей приходилось строить свою шифровальную службу по ходу дела. Иногда скорость и простота была важнее безопасности, нужно было хоть как-нибудь передать данные агентам.
Для этого использовали самое не технологичное и уязвимое шифрование Второй Мировой Войны - Поэмный код.

Для этого выбиралась известная английская поэма, например что-то из Шекспира, и ее слова использовались как ключ.

Проблемы очевидны:

  • Поэму можно угадать, она всегда известная.

  • Выбить под пытками ее легко, это не блокнот, уничтожив который агент при всем желании не может сдать ключ.

  • Слова поэмы не должны повторяться, а количество различных слов в поэме ограничено, всегда есть соблазн повторить.

  • Узнав, что за поэма использовалась, ты получаешь доступ и ко всем последующим сообщениям и ко всем предыдущим. Вся переписка с этой агентурной ячейкой скомпрометирована.

  • Длина сообщения также должна была быть не более 200 символов из-за опасности частотного анализа.

Основной метод взлома Поэмного кодаОсновной метод взлома Поэмного кода

Данный шифр вскрывался регулярно, британцы это понимали и использовали его в самых крайних случаях, например для шифрования сообщений стихийно сформированных в тылу противника группам агентов. Для действительно стойкого шифрования в Великобритании наиболее массово использовалась криптографическая машинаTypex(она жеType X,Type 10).
Она была изобретена в 1937 году, на базе коммерческой версии немецкой тактической машиныЭнигма. Просуществовала Typex до середины 1950-ых годов.

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

Такая самоуверенность в собственном продукте сыграло с Германией очень злую шутку. Если бы немцы усомнились в надежности Энигмы и соответственно Typex, провели достаточно исследований и смогли бы взломать собственный же алгоритм То они бы не только получили контроль над самыми важными коммуникациями Британии, но и смогли бы защитить Энигму от будущего взлома.

Более того, английские операторы совершали ошибки, как и все люди, которые могли немцам в помочь во взломе, но В итоге Typexне взломана, из-за самоуверенности криптоаналитиков Германии. США же неоднократно указывали британцам на возможные проблемы Typex и сами британцы старались модифицировать машину, поднимая ее криптостойкость.

Typex слева сообщение, справа шифровкаTypex слева сообщение, справа шифровка

После 1943 года Великобритания начала переходить на две другие машины:

  • Rockex- которая преимущественно использовалась между ими и Канадой. Поэтому не представляла особого интереса в рамках Второй Мировой Войны.

  • CCM(Combined Cipher Machine) -Комбинированная Шифровальная Машина. Созданная в США в 1943 году для коммуникации между Великобританией и США.

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

После войны использовалась в блоке НАТО, пока не была модифицирована в машину AJAX. Английское шифрование было самым надежным на криптографическом фронте, ну, если не считать Поэмный код =).

Атака

Об успехах Великобритании по расшифровке сообщений Германии сложно не услышать, особенно обАлане Тьюрингеи взломе немецкой Энигмы, однако многие очень важные подробности часто упускаются из вида.

С момента вступления Великобритании во Вторую Мировую Войну одним из самых важных и засекреченных мест сталаStation X. Он же особнякБлетчли-паркв городке Милтон Хилс, где располагалось главное шифровальное подразделение Великобритании. Именно в Блетчли-парк лучшие ученые работали над взломом шифровальных машин Германии, среди которых были и внедренные агенты советской разведки, о которых я говорил в первой части.

Ламповый офис для лучших умовЛамповый офис для лучших умов

Основных направлений было два: одно было нацелено на взлом немецкой Энигмы, второе - на взлом немецкойМашины Лоренца(она жеТанни). Про взлом Энигмы существует множество статей и обзоров, поэтому я расскажу кратко по основным вехам.

До Великобритании во взломе Энигмы уже были сделаны немалые успехи:

  • Польская разведка выкупила коммерческую версию Энигмы (узнав о ней из патентов) и обнаружила в ней ряд недостатков еще перед началом Второй мировой войны. Более того, они предложили метод подбора положений роторов, о возможности которого немцы не догадывались и не смогли повторить во взломе Typex.

  • В декабре 1932 года польский криптографМариан Реевскийполучил кодовые книги Германии на 2 месяца. Данные материалы позволили восстановить внутреннюю электропроводку роторов и построить военный вариант Энигмы из коммерческого.

  • 15 декабря 1938 года Германия добавила два новых ротора в военную версию, а 1 января 1939 года увеличила количество соединений коммутационной панели. Всё это значительно затруднило будущий криптоанализ Энигмы.

  • С началом войны и падением Польши исследователи успели передать свои успехи французам, которые попытались развить их. Но после скорого падения Франции материалы разработок поляков и французов передали в Бетчли-Парк.

18 марта 1940 года в Бетчли-Парке на основе неоднократно захваченных Энигм и разработок польских криптографов Аланом Тьюрингом была построена электронно-механическая машинаBombe (Бомба). Задача этой машины была проста: перебирать ежедневно меняющиеся ключи шифрования, если известна структура сообщения или какая-то его часть. В криптографии эта атака называется Атакой на основе открытых текстов.

Алан Тьюринг и его творение.

То есть криптоаналитикам было достаточно хотя бы одного частично известного сообщения в день, чтобы расшифровать все остальные сообщения в этот же день. Но его еще нужно было получить, нужны были подсказки.
Часто сообщения можно было предугадать, можно было догадаться по времени, месту, ситуации о куске передаваемых сообщений. Догадаться, что в начале/конце сообщения идет типичное нацистское приветствие или дата.

Прогнозы погоды, например, передавали регулярно и в одно и тоже время, они всегда содержали слово погода, да и британские метеослужбы знали о погоде в регионе. Постоянно передаваемые сообщения одной длины намекали, что это какая-то стандартная регламентированная фраза, например Нечего сообщить.

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

Узнав об этом Алан Тьюринг пересмотрел все доступные сообщения и обнаружил, что самым распространенным числом встречаемым в 90% сообщений является 1 - eins. И на основе всех комбинаций написания числа eins можно успешно осуществлять атаку по подбору ключа.

Но что если ключ никак не подбирается с утра, нет никаких подсказок, а предстоит важнейшая операция?

Тогда остается одно из любимейших занятий разведки - провокация! Например, минирование определенного участка моря на виду у противника. А дальше ожидание сообщения точно содержащего эти координаты. Таким образом успешно взламывалась Энигма, хотя немцы несколько раз незначительно меняли ее внутреннее устройство и приходилось захватывать ее заново. О работе отдела по взлому Энигмы сняли множество фильмов, а вот о втором говорили гораздо меньше.

Отдел по взлому Машины Лоренца получили гораздо более сложную задачу: взломать криптографическую машину, которую они никогда не видели, которую ни разу не захватывали, патентов на которую не было в открытом доступе. Это было практически невозможно.

Разведкой Великобритании были построены дополнительные станции для перехвата сообщений, но шифр никак не поддавался расшифровке. Пока не сыграл человеческий фактор. 30 августа 1941 года из Афин в Вену было передано сообщение, и первый раз передача не прошла. Оператора по открытому каналу попросили передать сообщение снова, и все что ему требовалось - повторить свои действия, зашифровать тоже самое сообщение тем же самым ключом и заново отправить.

Однако оператор неумышленно его изменил, использовал аббревиатуры и к тому же пару раз опечатался. Он знал, что пересылать два разных сообщения под одним ключем было СТРОЖАЙШЕ запрещено, но даже не подумал, что так незначительно изменяя сообщение он ставит под угрозу все шифрование Германии. Сотрудники разведки, перехватившие оба сообщения различной длины и запрос на повтор поняли, что оператор допустил ошибку и немедленно передали шифровки в Блетчли-парк.

А уже там криптографДжон Тилтмани его команда приступили к расшифровке, применяя ту же самую атаку на основе открытого текста, подобрав часть сообщения из-за ошибки оператора. Через некоторое время оба сообщения были расшифрованы, но это было только начало.

Уильям Татт истинный гений Блетчли-паркаУильям Татт истинный гений Блетчли-парка

В октябре 1941 года к их команде присоединился гениальный криптоаналитикУильям Татт. И команда совершила невозможное - они восстановили логику работы Машины Лоренца. Восстановили методом обратной разработки, зная 2 сообщения разной длины и подобранный ключ. Таким образом была взломана самая надежная машина Германии без ее захвата, без кражи ключей шифрования и какой либо информации о ней на одной единственной ошибке оператора.

Восстановление алгоритма Машины Лоренца при вручении Татту ордена Канады назвали одно из величайший интеллектуальных достижений Второй Мировой Войны.

Это ни шло ни в какое сравнение с тем, что сделал Тьюринг с Энигмой, но об этом не так активно говорят.

Автоматизировал расшифровку Машины ЛоренцаМакс Ньюман, а реализовал проект инженер, которого Тьюринг уже использовал при создании Бомбы -Томас Флоуэрс. Так родился первый компьютер -Colossus(Колоссус), который полностью автоматизировал процесс подбора ключей к Машине Лоуренса.

Этот компьютер и был изображен на заглавной картинке статьи, его создатели:

  • Уильям Татт (алгоритм расшифровки)

  • Макс Ньюман (архитектура)

  • Томас Флоуэрс (реализация)

ColossusColossus

Таким образом двумя отделами Блетчли-парка были взломаны обе шифровальные машины Германии в большинстве случаев, и был автоматизирован процесс подбора ключей к ним.

США

Защита

США использовали кучу различных систем шифрования между ведомствами (напримерSIGABA), отдельную систему CCM для связи с Великобританией, о которой мы говорили ранее; все они так ине были взломаны.

M-209 в музее.

А вот тактическая полевая шифровальная машина у них была одна -M-209.
Эта машина активно использовалась на фронтах и была неоднократно захвачена. Она имела ключевую уязвимость в виде записи цифр буквами, как в Энигме. Это позволяло искать подсказки по числам, как это делал Тьюринг. По недавно рассекреченным данным АНБ она была взломана лишь от 10 до 30% случаев. Но это было не так. В разделе атака Германии я это опровергну и расскажу об этом подробнее.М-209была взломана в 100% случаев.

Судя по всему, об успехах Германии по взлому М-209 американцам не стало известно и после войны, так как эту машину использовали даже во время Войны во Вьетнаме.

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

Но были и фундаментальные проблемы у такого метода защиты информации:

  • Таких шифровальщиков было довольно мало, их приходилось с одной стороны беречь, с другой, убивать при малейшей опасности, чтоб их не захватили в плен.

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

Полноценным шифрованием это не назвать, но для использования на фронте годилось в ряде случаев. В 2002 году о шифровальщиках-навахо даже сняли художественный фильм Говорящие с ветром.

Атака

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

Известно, что в битвах за острова архипелага Сайпан (15.06 - 9.07 1944) американцами были захвачены самые защищенные шифровальные машины Японии Jade. И на их основе была построенаRATTLER- электрическая машина для автоматического подбора ключей. Таким образом при наличии подсказок Jade-шифрование было взломано во всех случаях и процесс взлома был полностью автоматизирован. Технические характеристики, авторы и особенности реализации неизвестны.

Германия

Защита

Энигма- тактическая шифровальная машина, использовалась в основном в полевых условиях - на фронте.Была взломана(Польша, Великобритания). Подробнее в разделе атака Великобритании.

Стоит выделить основные причины довольно быстрого взлома Энигмы:

  • Распространяемая до войны коммерческая версия.

  • Частые захваты машины с установленными роторами.

  • Самоуверенность немцев и как следствие отсутствие фундаментальных модификаций машины в процессе войны.

  • Человеческий фактор

Энигма фото из музеяЭнигма фото из музея

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

Машина Лоренца из музеяМашина Лоренца из музея

Атака

До недавнего времени было известно в основном о провалах немецких криптоаналитиков. Да, они легко взломали тактическую машину СССР, но ее быстро вывели из эксплуатации на западном фронте. Они не усомнились в собственной Энигме, а ведь могли и ее уберечь, и вскрыть британский Typex!

Но был и успех, о котором ранее еще не писали в англоязычных и тем более русскоязычных источниках - была взломана американская тактическая машина М-209. Взломана во всех случаях из-за кодирования словами отдельных цифр. Таким же методом подбора, как это делал Тьюринг, о котором немцам известно не было.

Рейнольд Вебер за написание мемуаров в 2000 году.Рейнольд Вебер за написание мемуаров в 2000 году.

Человек взломавший М-209 -Рейнольд Веберв 2000 году написал об этом мемуары для внуков. Там он подробно рассказал, как именно шла работа немецких криптоаналитиков.

Как оказалось, они не уступали ни американским, ни британским коллегам.
В апреле 1944 года, за 2 месяца до высадки союзников в Нормандии, была начата работа по созданию немецкой электромеханической машины по автоматизированному подбору ключей.

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

И в конце августа 1944 года отдел Рейнольда Вебера собственноручно собрал эту машину и продемонстрировал ее работу на американских шифровках.
За 7 часов работы машина подобрала ключ ко всем сообщениям армии США на эти сутки.

Но эффективно использовать ее так и не смогли, было уже слишком поздно. Эта машина даже официального названия не получила. Раз за разом команда Вебера получала приказы на передислокацию. Из-за постоянного отступления войск и смещения фронта, машину даже не успевали развернуть и подключить.
А в начале 1945 года ее было приказано уничтожить. На тот момент Вебер уже был один, команда дезертировала. Так первый немецкий компьютер был уничтожен с помощью пилы, кирки, молотка и топора. И забыт.

Итог

На криптографическом фронте было не менее горячо, чем на реальном фронте. Это не было игрой в одни ворота. Использовались все доступные методы шифрования, удачные и не очень. От Поэмного кода и шифровальщиков-навахо, до Машины Лоренца и CCM, от Энигмы и Кристалла, до Orange и Jade.

Было создано 4 автоматизированных машины для взлома шифров. Они - предки современных компьютеров:

  • Bombe (Великобритания)

  • Colossus (Великобритания)

  • Rattler (США)

  • И немецкая безымянная машина (Германия)

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

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

Криптография - дочка математики. Она может быть идеальной, а люди нет.

На последок вот вам достижения криптографического фронта:

  • Альтернативные методы взлома- СССР (Резидентура в Японии и Кембриджская пятерка).

  • Захвачен, но не взломан- Великобритания (Typex).

  • Взлом по воздуху- Великобритания (Машина Лоренца взлом без захвата).

  • Очень темная лошадка- Германия (Компьютер для взлома М-209).

  • Да быть того не может!- Япония (Слепая вера в неуязвимость собственного шифрования).

  • Джонни, они не взломают!- США (Использовать уже взломанную машину в другой войне)

Автор: Forbidden World

Оригинал

Подробнее..

Recovery mode Элементарная гигиена и слив базы сторонников Навального

16.04.2021 14:07:36 | Автор: admin

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

Я не очень в курсе, для чего вообще хранились почтовые адреса людей, ведь нужны они были разве что для того, чтобы получить подтверждение, что почта существует и человек не фиктивный, после чего её можно было смело удалить. Но раз нужно, так нужно.

Для такого случая отлично бы подошло любое асимметричное шифрование. Суть его, я полагаю, большинству известна. А кому не известна, - я поясню. Асимметричное шифрование - это такой способ шифрования, при котором для зашифровки используется один ключ, а для расшифровки - другой. Первый ключ мы используем для шифрования данных (в том числе имэйлов) - потерять его вообще не страшно, его можно выложить хоть в открытый доступ, потому что расшифровать с помощью него ничего не получится. Второй же ключ - храним в очень тайном месте и никому не говорим о нём, и, тем более, не выкладываем его на сервер, который могут взломать. Когда набралось бы ожидаемые пол миллиона человек, можно было бы достать ключ для расшифровки и получить список адресов из БД, например, для рассылки о том, что "пора".

Создатели же системы, судя по всему, понадеялись на свои силы и на авось. Как результат: сотни тысяч подтверждённых активных почтовых ящиков в открытом доступе. Кто ими воспользуется: мошенники для рассылки спама или противоборствующая сторона для давления - одному богу известно. Не надо так делать.

Подробнее..

Demhack 2 пришел, напрогал, победил

20.04.2021 12:16:22 | Автор: admin

20 и 21 марта 2021 года прошел хакатон проектов в сфере приватности и открытости информации DemHack 2, организованный Роскомсвободой и Privacy Accelerator. Хакатон собрал интересные идеи и талантливых разработчиков, выявил несколько по-настоящему перспективных проектов и наградил два из них! Некоторые решения были высоко оценены жюри и менторами, что дает им отличные шансы на дальнейшую экспертную поддержку, нетворкинг и дальнейшее сотрудничество с бизнес-инвесторами или профильными НКО.

Как готовился и проходил DemHack 2

Demhack 2 - второй - потому что первый состоялся осенью 2020 года. Тогда победителями стали три команды, одна из которых, Amnezia VPN, уже через полгода зарелизила готовое решение - сервис для создания личного VPN на собственном сервере. Проект после хакатона участвовал в Privacy Accelerator и прокачался от идеи до выпуска готового продукта. Сейчас команда Amnezia исправляет баги, дорабатывает новые фичи и готовится выйти на международный рынок!

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

Еще одним важным этапом подготовки хакатона стали консультации с экспертами, которые участвовали в фокус-группах и обсуждали актуальные вызовы в сфере прайваси и доступа к информации. По итогам этих обсуждений был сформирован список задач, которые команды могли взять за основу для решения на хакатоне. И многие команды с успехом воспользовались возможностью. Например, в треке Приватность и цифровая безопасность можно было взять задачу и разработать трекер привычек по информационной безопасности или систему проверки разрешений для установленных мобильных приложений, систему обнаружения взлома мессенджеров и пр. А в треке Доступ к информации и свобода передачи данных предлагалось придумать, как мониторить использование технологий слежки за гражданами на основе анализа госзакупок, документировать шатдауны или разрабатывать технологии Internet over gsm.

Кроме списка задач от экспертов, участники могли предложить собственные идеи. Предложенные проекты проходили специальный отбор конкурсной комиссии, которая оценивала их перспективность, а также соответствие ценностям хакатона и актуальность поднятой проблемы.

На хакатон было подано более 70 заявок, которые пришли из из 21 региона России, а также Сербии, Таджикистана, Республики Беларусь, Украины.Сформировалось и приступило к работе на хакатоне 15 команд.

Хакатон проводился полностью онлайн от момента открытия до объявления победителей. Субботним утром 20 марта состоялся старт конкурса, команды встретились со своими наставниками - ведущими менторами, которые сопровождали их на протяжении всего хакатона и следили за развитием проекта. Помимо наставников, команды встречались с узкоспециализированными экспертами. А уже в конце второго дня команды заполнили финальные чек-поинты, сформировали презентацию и вышли на питчинг перед жюри. После презентаций судьи назвали лучшие проекты, а организаторы распределили призы.

Победители хакатона Demhack 2

В треке Прайваси и цифровая безопасность победителем стал проект Security Addon, в лице единственного члена, юного разработчика Захарова Алексея. Он спроектировал приложение, запускающее безвозвратное удаление и шифрование папок в экстренных ситуациях, когда безопасность и приватность на первом месте. Приложение предназначено для использования широким кругом лиц в пов

В треке Доступ к информации и свобода передачи данных лучшими стали ребята из команды mr.bot, предложившие проект Deep Silent, куда входят два андроид-приложения: первое - кнопка SOS делает звонок абоненту и передает DTMF-код; второе - умеет кодировать информацию с помощью высокого и низкого сигнала принимать и обрабатывать ее.

Победитель Security Addon получил приглашение стать резидентом второго набора Privacy Accelerator. А с командой mr.bot взялись далее сотрудничать менторы, в том числе для того, чтобы помочь им найти поддержку заинтересованных организаций и выйти на целевую аудиторию.

Некоторые перспективные проекты хакатона DemHack 2 также получили приглашение в акселератор.

Среди них Nemezida DNT (второе место в треке Прайваси и цифровая безопасность), плагин для браузеров, функционирующих на основе Chromium, который позволяет обеспечить максимальную защиту пользователя от сбора информации о нем и его устройстве. К сожалению, у команды сейчас нет ссылки на активный гитхаб. А также проект Соlaba (третье место в треке Прайваси и цифровая безопасность), который позволяет проверить себя на умение распознавать фишинговые рассылки, проверить свою команду на устойчивость к фишинговым атакам.

Второе место в треке Доступ к информации и свобода передачи данных занял проект Shutdown Scout, веб-сервис для определения скорости соединения и наличия блокировки сетевых ресурсов для каждой страны мира. Третьи в этом треке - команда UNDEFINED, которая разработала систему мониторинга инфраструктуры слежки за гражданами в виде общедоступного ресурса с визуализацией данных.

Проект Kitty Cloud не занял почетного места, но был высоко оценен менторами и судьями хакатона, что дало ему возможность также попасть в новый набор Privacy Accelerator. Проект представляет собой облачное хранилище данных с полным p2p-шифрованием.

Что будет дальше

Новый хакатон не за горами! Мы уже сейчас начинаем готовиться к нему и планируем вскоре встретиться с нашими экспертами для обсуждения новых угроз и трансформаций в сфере прайваси и доступа к информации. Мы обновим карту вызовов и придумаем новые форматы работы.

А еще мы надеемся, что новый хакатон будет - наконец! - оффлайн. И мы сможем не только хорошо поработать и сделать крутые проекты, но и славно потусить все вместе. Как в старые добрые!

Подробнее..

Как мы добавляли CPU флаги Intel SGX в libvirt

22.04.2021 14:23:53 | Автор: admin
С момента публикации статьи о внедрении Intel SGX в наше публичное облако прошло несколько месяцев. За это время решение было существенно доработано. В основном улучшения касаются устранения мелких багов и доработок для нашего же удобства.



Есть, однако, один момент, о котором хотелось бы рассказать подробнее.



В предыдущей статье мы писали, что в рамках реализации поддержки SGX нужно было научить сервис Nova генерировать XML-файл с необходимыми настройками гостевого домена. Проблема эта оказалась сложной и интересной: за время работы по её решению нам пришлось на примере libvirt подробно разбираться, как в целом программы взаимодействуют с наборами инструкций в процессорах x86. Подробных и самое главное понятно написанных материалов на эту тему очень и очень немного. Надеемся, что наш опыт будет полезен всем, кто занимается виртуализацией. Впрочем, обо всём по порядку.

Первые попытки


Ещё раз повторим формулировку задачи: нам требовалось передать параметры поддержки SGX в конфигурационный XML-файл виртуальной машины. Когда мы только начинали эту задачу решать, в OpenStack и libvirt поддержки SGX не было, соответственно, передать их в XML виртуальной машины нативно было невозможно.

Сначала мы попытались решить эту проблему путем добавления блока Qemu command-line в скрипт подключение к гипервизору через libvirt, как это описано в руководстве Intel для разработчиков:

<qemu:commandline>     <qemu:arg value='-cpu'/>     <qemu:arg value='host,+sgx,+sgxlc'/>     <qemu:arg value='-object'/>     <qemu:arg value='memory-backend-epc,id=mem1,size=''' + epc + '''M,prealloc'/>     <qemu:arg value='-sgx-epc'/>     <qemu:arg value='id=epc1,memdev=mem1'/></qemu:commandline>

Но после этого у виртуальной машины добавилась вторая процессорная опция:

[root@compute-sgx ~] cat /proc/$PID/cmdline |xargs -0 printf "%s\n" |awk '/cpu/ { getline x; print $0 RS x; }'-cpuSkylake-Client-IBRS-cpuhost,+sgx,+sgxlc

Первая опция задавалась штатно, а вторая была непосредственно нами добавлена в блоке Qemu command-line. Это приводило к неудобству при выборе модели эмуляции процессора: какую бы из моделей процессоров мы ни подставляли в cpu_model в конфигурационном файле вычислительного узла Nova, в виртуальной машине мы видели отображение хостового процессора.

Как решить эту проблему?

В поисках ответа мы сначала пробовали экспериментировать со строкой <qemu:arg value='host,+sgx,+sgxlc'/> и пытаться передать в неё модель процессора, но это не отменяло дублирование этой опции после запуска ВМ. Тогда было решено задействовать libvirt для присвоения флагов CPU и управлять ими через конфигурационный файл Novы вычислительного узла с помощью параметра cpu_model_extra_flags.

Задача оказалась сложнее, чем мы предполагали: нам потребовалось изучить инструкцию Intel IA-32 CPUID, а также найти информацию о нужных регистрах и битах в документации Intel об SGX.

Дальнейший поиск: углубляемся в libvirt


В документации для разработчиков сервиса Nova указано, что маппинг CPU флагов должен поддерживаться самим libvirtом.

Мы нашли файл, в котором описываются все флаги CPU это x86_features.xml (актуален с версии libvirt 4.7.0). Ознакомившись с этим файлом, предположили (как выяснилось потом, ошибочно), что нам нужно лишь получить hex-адреса необходимых регистров в 7-м листе с помощью утилиты cpuid. Из документации Intel мы узнали, в каких регистрах вызываются нужные нам инструкции: sgx находится в EBX регистре, а sgxlc в ECX.

[root@compute-sgx ~] cpuid -l 7 -1 |grep SGX      SGX: Software Guard Extensions supported = true      SGX_LC: SGX launch config supported      = true[root@compute-sgx ~] cpuid -l 7 -1 -rCPU:   0x00000007 0x00: eax=0x00000000 ebx=0x029c6fbf ecx=0x40000000 edx=0xbc000600

После добавления флагов sgx и sgxlc со значениями, полученными с помощью утилиты cpuid, мы получили следующее сообщение об ошибке:

error : x86Compute:1952 : out of memory

Сообщение, прямо говоря, не очень информативное. Чтобы хоть как-то понять, в чём проблема, мы завели issue в gitlabe libvirta. Разработчики libvirta заметили, что выводится неверная ошибка и исправили ее, указав на то, что libvirt не может найти нужную инструкцию, которую мы вызываем и предположили, где мы можем ошибаться. Но понять, что именно нам нужно было указывать, чтобы ошибки не было, нам так и не удалось.

Пришлось зарыться в источники и изучать, это заняло много времени. Разобраться удалось лишь после изучения кода в модифицированном Qemu от Intel:

    [FEAT_7_0_EBX] = {        .type = CPUID_FEATURE_WORD,        .feat_names = {            "fsgsbase", "tsc-adjust", "sgx", "bmi1",            "hle", "avx2", NULL, "smep",            "bmi2", "erms", "invpcid", "rtm",            NULL, NULL, "mpx", NULL,            "avx512f", "avx512dq", "rdseed", "adx",            "smap", "avx512ifma", "pcommit", "clflushopt",            "clwb", "intel-pt", "avx512pf", "avx512er",            "avx512cd", "sha-ni", "avx512bw", "avx512vl",        },        .cpuid = {            .eax = 7,            .needs_ecx = true, .ecx = 0,            .reg = R_EBX,        },        .tcg_features = TCG_7_0_EBX_FEATURES,    },    [FEAT_7_0_ECX] = {        .type = CPUID_FEATURE_WORD,        .feat_names = {            NULL, "avx512vbmi", "umip", "pku",            NULL /* ospke */, "waitpkg", "avx512vbmi2", NULL,            "gfni", "vaes", "vpclmulqdq", "avx512vnni",            "avx512bitalg", NULL, "avx512-vpopcntdq", NULL,            "la57", NULL, NULL, NULL,            NULL, NULL, "rdpid", NULL,            NULL, "cldemote", NULL, "movdiri",            "movdir64b", NULL, "sgxlc", NULL,        },        .cpuid = {            .eax = 7,            .needs_ecx = true, .ecx = 0,            .reg = R_ECX,        },

Из приведенного листинга видно, что в блоках .feat_names побитово (от 0 до 31) перечисляются инструкции из EBX/ECX-регистров 7-го листа; если инструкция не поддерживается Qemu или этот бит зарезервирован, то он заполняется значением NULL. Благодаря этому примеру мы сделали такое предположение: возможно, нужно указывать не hex-адрес необходимого регистра в libvirt, а конкретно бит этой инструкции. Проще это понять, ознакомившись с таблицей из Википедии. Слева указан бит и три регистра. Находим в ней нашу инструкцию sgx. В таблице она указана под вторым битом в регистре EBX:



Далее сверяем расположение этой инструкции в коде Qemu. Как мы видим, она указана третьей в списке feat_names, но это потому, что нумерация битов начинается от 0:

    [FEAT_7_0_EBX] = {        .type = CPUID_FEATURE_WORD,        .feat_names = {            "fsgsbase", "tsc-adjust", "sgx", "bmi1",

Можно посмотреть другие инструкции в этой таблице и убедиться при подсчете от 0, что они находятся под своим битом в приведенном листинге. Например: fsgsbase идет под 0 битом регистра EBX, и он указан в этом списке первым.

В документации Intel мы нашли этому подтверждение и убедились, что необходимый набор инструкций можно вызвать с использованием cpuid, передавая правильный бит при обращении к регистру нужного листа, а в некоторых случаях подлиста.

Мы стали более подробно разбираться в архитектуре 32-битных процессоров и увидели, что в таких процессорах имеются листы, которые содержат основные 4 регистра: EAX, EBX, ECX, EDX. Каждый из этих регистров содержит по 32 бита, отведенных под определенный набор инструкций CPU. Бит является степенью двойки и чаще всего может передаваться программе в hex-формате, как это сделано в libvirt.

Для лучшего понимания, рассмотрим еще пример c флагом вложенной виртуализации VMX из файла x86_features.xml, используемый libvirtом:

<feature name= 'vmx'>
<cpuid eax_in='0x01' ecx='0x00000020'/> # 25 = 3210 = 2016
</feature>

Обращение к этой инструкции осуществляется в 1-м листе к регистру ECX под 5 битом и убедиться в этом можно посмотрев таблицу Feature Information в Википедии.

Разобравшись с этим и сформировав понимание, как в итоге добавляются флаги в libvirt, мы решили добавить и другие флаги SGX (помимо основных: sgx и sgxlc), которые имелись в модифицированном Qemu:

[root@compute-sgx ~] /usr/libexec/qemu-kvm -cpu help |xargs printf '%s\n' |grep sgxsgxsgx-debugsgx-exinfosgx-ksssgx-mode64sgx-provisionkeysgx-tokenkeysgx1sgx2sgxlc

Некоторые из этих флагов являются уже не инструкциями, а атрибутами структуры управления данными анклавов (SECS); подробнее об этом можно прочитать в документации Intel. В ней мы нашли, что необходимый нам набор атрибутов SGX находится в листе 0x12 в подлисте 1:

[root@compute-sgx ~] cpuid -l 0x12 -s 1 -1CPU:   SGX attributes (0x12/1):      ECREATE SECS.ATTRIBUTES valid bit mask = 0x000000000000001f0000000000000036



На скриншоте таблицы 38-3 можно найти необходимые нам биты атрибутов, которые мы укажем позже в качестве флагов в libvirt: sgx-debug, sgx-mode64, sgx-provisionkey, sgx-tokenkey. Они находятся под битами 1, 2, 4 и 5.

Так же мы поняли из ответа в нашем issue: libvirt имеет макрос проверки флагов на предмет их поддержки непосредственно процессором вычислительного узла. Это означает то, что недостаточно указать в документе x86_features.xml необходимые листы, биты и регистры, если сам libvirt не поддерживает лист набора инструкций. Но к нашему счастью выяснилось, что в коде libvirta имеется возможность работы с этим листом:

/* Leaf 0x12: SGX capability enumeration * * Sub leaves 0 and 1 is supported if ebx[2] from leaf 0x7 (SGX) is set. * Sub leaves n >= 2 are valid as long as eax[3:0] != 0. */static intcpuidSetLeaf12(virCPUDataPtr data,               virCPUx86DataItemPtr subLeaf0){    virCPUx86DataItem item = CPUID(.eax_in = 0x7);    virCPUx86CPUIDPtr cpuid = &item.data.cpuid;    virCPUx86DataItemPtr leaf7;    if (!(leaf7 = virCPUx86DataGet(&data->data.x86, &item)) ||        !(leaf7->data.cpuid.ebx & (1 << 2)))        return 0;    if (virCPUx86DataAdd(data, subLeaf0) < 0)        return -1;    cpuid->eax_in = 0x12;    cpuid->ecx_in = 1;    cpuidCall(cpuid);    if (virCPUx86DataAdd(data, &item) < 0)        return -1;    cpuid->ecx_in = 2;    cpuidCall(cpuid);    while (cpuid->eax & 0xf) {        if (virCPUx86DataAdd(data, &item) < 0)            return -1;        cpuid->ecx_in++;        cpuidCall(cpuid);    }    return 0;}

Из этого листинга видно, что при обращении к 2-му биту EBX регистра 7-го листа (т.е. к инструкции SGX), libvirt может задействовать лист 0x12 для проверки имеющихся атрибутов в подлистах 0, 1 и 2.

Заключение


После проделанного исследования мы поняли, как правильно дополнить файл x86_features.xml. Мы перевели необходимые биты в hex-формат и вот что у нас получилось:

  <!-- SGX features -->  <feature name='sgx'>    <cpuid eax_in='0x07' ecx_in='0x00' ebx='0x00000004'/>  </feature>  <feature name='sgxlc'>    <cpuid eax_in='0x07' ecx_in='0x00' ecx='0x40000000'/>  </feature>  <feature name='sgx1'>    <cpuid eax_in='0x12' ecx_in='0x00' eax='0x00000001'/>  </feature>  <feature name='sgx-debug'>    <cpuid eax_in='0x12' ecx_in='0x01' eax='0x00000002'/>  </feature>  <feature name='sgx-mode64'>    <cpuid eax_in='0x12' ecx_in='0x01' eax='0x00000004'/>  </feature>  <feature name='sgx-provisionkey'>    <cpuid eax_in='0x12' ecx_in='0x01' eax='0x00000010'/>  </feature>  <feature name='sgx-tokenkey'>    <cpuid eax_in='0x12' ecx_in='0x01' eax='0x00000020'/>  </feature>

Теперь для передачи этих флагов виртуальной машине мы можем указать их в конфигурационном файле Nova с помощью cpu_model_extra_flags:

[root@compute-sgx nova] grep cpu_mode nova.confcpu_mode = customcpu_model = Skylake-Client-IBRScpu_model_extra_flags = sgx,sgxlc,sgx1,sgx-provisionkey,sgx-tokenkey,sgx-debug,sgx-mode64[root@compute-sgx ~] cat /proc/$PID/cmdline |xargs -0 printf "%s\n" |awk '/cpu/ { getline x; print $0 RS x; }'-cpuSkylake-Client-IBRS,sgx=on,sgx-mode64=on,sgx-provisionkey=on,sgx-tokenkey=on,sgx1=on,sgxlc=on

Проделав сложный путь, мы научились добавлять в libvirt поддержку флагов SGX. Это нам помогло решить проблему дублирования процессорных опций в XML-файле виртуальной машины. Полученный опыт мы будем использовать и в дальнейшей работе: если в процессорах Intel или AMD появится новый набор инструкций, мы сможем их аналогичным образом добавить в libvirt. Знакомство с инструкцией CPUID так же будет нам полезно при написании своих собственных решений.

Если у вас есть вопросы добро пожаловать в комментарии, постараемся ответить. А если у вас есть, что дополнить тем более пишите, будем очень признательны.
Подробнее..

Честное онлайн-голосование миф или реальность?

27.05.2021 22:07:59 | Автор: admin

Привет, Хабр! Меня зовут Иван, я разрабатываю сервис онлайн-голосований WE.Vote на основе блокчейн-платформы Waves Enterprise. Сама идея голосований в онлайне уже давным-давно реализована разными компаниями, но в любых кейсах повышенной ответственности все равно прибегают к старой доброй бумаге. Давайте посмотрим, как электронное голосование сможет посостязаться с ней в максимально строгих условиях.

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

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

Подавляющее большинство сервисов онлайн-голосований это централизованные решения, управляемые конкретной компанией-оператором. Есть ли здесь угрозы конфиденциальности и манипуляций? С технической точки зрения понятно, что оператор такой системы имеет доступ ко всем ее данным. Соответственно, теоретически у него есть возможность вмешаться в ход голосования или подправить результаты. Но многие решения, используемые бизнесом, опираются на доверие ив этом нет ничего страшного.

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

Но что если вам нужно больше?Что если одновременно нужна экономическая эффективность, конфиденциальность принимаемых решений и максимальная теоретическая невозможность для оператора сервиса онлайн-голосований повлиять на их результат? Представьте гипотетическую ситуацию, в которой одна глобальная корпорация Кэппл принимает решения о своей глобальной стратегии развития на инфраструктуре другой глобальной корпорации Тубель.Сколько бы соглашений и договоров нибыло подписано удержится ли Тубель от возможности подсмотреть, о чем там договаривается его прямой или косвенный конкурент? Будет ли менеджмент Кэппла спать спокойно, будучи уверенным, что никто не вмешается в их прорывные планы развития?

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

Чего мы хотим добиться

На самом абстрактном уровне всё, что делают информационные системы, это создают/изменяют данные, передают данные, хранят данные, а также дают доступ к этим данным.В применении к онлайн-голосованиям это выглядит следующим образом:

  1. Организатор голосования создает повестку и хочет управлять правилами доступа к этой повестке: сделать ли ее публичной или раскрыть только участникам голосования.

  2. Организатор голосования создает список его участников и хочет быть уверенным, что в его голосовании примут участие именно они. Сами участники также заинтересованы в этом, так как подразумевается, что они принимают решение по важному для них вопросу и мнение/намерения/выгода третьих лиц не должны на это решение повлиять.

  3. Организатор задает правила проведения голосования. Эти правила без раскрытия сути голосования должны быть доступны широкой публике, чтобы организатор, участники голосования и третьи лица (например, регулятор) могли убедиться, что способ принятия решения был выбран корректно и все прошло по заранее известному плану.

  4. Участник голосования получает повестку голосования и хочет убедиться, что это именно тот список вопросов, который получат все остальные, что ему не прислали укороченную версию. Эти сомнения выглядят нелепо для бумажного голосования, где все бланки заранее распечатаны, все выдаются из общей стопки и нет никакой возможности персонализировать бюллетень. Но в случае онлайн-голосования все, что мы имеем, это байты информации, которые легко изменить на лету. Поэтому хотелось бы иметь больше гарантий аутентичности.

  5. Участник голосования заполняет бюллетень и хочет убедиться, что его голос будет учтен, его голос не будет изменен и никто не сможет определить, как именно он проголосовал (частный случай открытых голосований оставим в стороне).

  6. Организатор и участники голосования узнают его результаты. В этот момент все хотятбыть уверены,что никто не вмешивался в ход голосования, не фильтровал бюллетени, не делал вбросов, не подправлял итоги и т.п.. Все включая и оператора онлайн-сервиса голосований.

При чем тут блокчейн

Если бегло пробежаться по первому разделу, то единственной проблемой онлайн-голосований выглядит всевластие оператора сервиса голосования. Хорошая новость в том, что с появлением блокчейна мы научились ограничивать эту власть, разделяя ответственность за данные системы между ее участниками. Но очевидно, что блокчейн не являетсясеребряной пулей. По сути, это сравнительно медленная и капризная база данных инструмент, имеющий определенные положительные свойства и некоторые недостатки. Чтобы полностью решить задачу, необходимо правильно определить роль блокчейна в системе, понять,чего еще нам недостает.И кажется, у нас получилось сложить кусочки пазла посмотрим как.

Распределенное хранение

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

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

Естественно, встает вопрос хранения конфиденциальных данных. Блокчейнсам по себене позволяет таких фокусов, поэтому нам надо разобраться,какие данные в системе являются открытыми, а какие приватными. Атакженайти методы защиты конфиденциальных данных. Например, при создании голосования мы имеем следующий набор данных:

  • повестка и материалы голосования;

  • контактные данные пользователей идентификатор пользователей в реальном мире (e-mail или номер телефона);

  • персональные данные пользователей в целом, в системе не требуются, но делают работу удобнее как для организатора голосования, так и для самих участников;

  • пара публичного и приватного ключей участника голосования работают как внутрисистемный идентификатор пользователя и дают возможность участвовать вголосовании.

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

Персональные и контактные данные хранятся на сервере в виде учетной записи пользователя.Публиковать их на блокчейне было бы неправильно, и мы этого делать не будем.На блокчейне список участников голосования будет сохранен в виде списка публичных ключей.

Пара ключей создается пользователем локально, на его персональном устройстве. Приватный ключ этого устройства не покидает, а публичный сохраняется набэкендекак параметр учетной записи. Организатор голосования работает со списком участников в виде Ф.И.О. и e-mail. При сохранении данных голосования в блокчейне туда же уходит список публичных ключей. Голос подписан ключом пользователя, и если публичный ключ отправителя есть в списке участников, мы принимаем бюллетень. Такая схема позволяет,с одной стороны,не светить персональными данными пользователей, а с другой сделать более прозрачной работу системы.

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

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

Такой бюллетеньможно только потерять.Но у нас нет никаких оснований потерять именно этот бюллетень, так как мы не знаем за что голосовал пользователь, да и пропажа бюллетеня очень легко обнаруживается как самим голосующим, так и сторонними наблюдателями. Ведь в блокчейне виден и список зарегистрировавшихся участников голосования, и отправленные ими бюллетени. А распределенная природа блокчейна позволяет участнику голосования напрямую отправить транзакцию в любой из блокчейн-узлов, минуя бэкенд и любых других посредников. Узел блокчейна совершенно не интересуется голосованиями или любыми другими процессами, которые мы пытаемся строить поверх блокчейн-сети. Всё, что интересует сеть, это корректный формат транзакции: если всё верно, то транзакция, а значит и голос, будут зарегистрированы в блокчейне.

Подводя промежуточный итог, можно сказать,что:

  • Мы решили проблему прозрачности и immutable-хранения исторических данных, используя блокчейн.

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

  • Работу с информацией об участниках голосования мы разделили. Персональные данные хранятся классическим образом, а взаимодействие происходит с уникальным блокчейн-идентификатором, от имени которого пользователь отправляет свой голос.

Но, несмотря на то что мы обещали обойтись без магии, она все-таки произошла. Для шифрования бюллетеней нам понадобился открытый ключ голосования, но никто не сказал, откуда он взялся! Очевидно, что это предельно важная часть всего процесса голосования и к нему нельзя отнестись легкомысленно. Еще более интересным кусочком пазла является приватный ключ, соответствующий открытому ключу голосования, так как именно с его помощью мы сможем получить итоги голосования.Настал момент шагнуть в область криптографии (которая для 99.9% людей не сильно отличается от магии).

Криптография

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

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

Несколько болеенадежным вариантом будет техника разделения приватного ключа после генерации хорошо известная схема разделения секрета Шамира. Ключевая пара создается, публичный ключ сохраняется в блокчейне как открытый ключ голосования, а приватный ключ разделяется на несколько частей, которые независимо хранятся доверенными участниками. Чтобы подвести итоги голосования, приватный ключ необходимо собрать и после этого расшифровать бюллетени. Если кто-то из доверенных участников заболел, схема Шамира предполагает возможность сбора приватного ключа меньшим количеством участников. То есть если ключ был разбит на N частей, собрать обратно его можно, используя K частей, где K < N.

Такой вариант выглядит гораздо более надежным и продвинутым, и это действительно так. Но есть нюансы. Во-первых, в промежуток времени между генерацией ключа и его разделением он является очевидной мишенью для злоумышленников и единой точкой отказа системы. Во-вторых, после сбора ключа мы получаем возможность расшифровать каждый индивидуальный бюллетень. Это не позволит нам их отфильтровать, так как они уже находятся в блокчейне и никуда оттуда не денутся. Но это позволит нарушить конфиденциальность голосования. На бэкенде есть связка Ф.И.О. и публичного ключа участника голосования, а в блокчейне связка публичного ключа и теперь уже расшифрованного бюллетеня. Мы знаем, как и кто проголосовал можем взять и, например, лишить премии неугодных.

Конечно, существуют механизмы разрыва первой связки персональных данных и открытого ключа через технику слепой подписи, но это очень своеобразный механизм, который необходимо правильно внедрить. При этом всё равно может сохраниться возможность вычислить по IP голосующего. Он приходит на авторизованный метод получать слепую подпись, а потом стучится на неавторизованный метод отправить голос. Формально во втором случае мы не знаем, кто именно к нам пришел, и опираемся только на проверку слепой подписи. Но у нас есть возможность сопоставить параметры устройства/браузера/соединения и понять, что это тот самый Иванов, который 5 минут назад получал у нас слепую подпись. Или представим похожую атаку на сопоставление по времени получения подписи и отправки голоса. Когда голосующие идут толпой по 500 человек в секунду, такая атака теряет свою эффективность, но при меньшей нагрузке вполне себе работает.

Попробуем сделать лучше?

Распределенная генерация ключа

Попробуем и сделаем. В этом нам поможет протокол конфиденциального вычисления, позволяющий нескольким участникам произвести вычисление, зависящее от тайных входных данных каждого из них, таким образом, чтобы ни один участник не смог получить никакой информации о чужих тайных входных данных. Этот протокол обеспечивает процесс шифрования, в котором несколько сторон участвуют в вычислении общегооткрытогоключа голосования и соответствующего ему набора приватных ключей. При этом приватные ключи исходно создаются независимо, участники протокола конфиденциального вычисления не обмениваются ими - мы избавились от единой точки отказа. При этом мы имеем возможность реализовать пороговую схему K из N, аналогично схеме Шамира.

Для формирования общегооткрытогоключа голосования (MainPubliсKey) используется алгоритм DKG (distributed key generation) из статьи TorbenPrydsPedersenA threshold cryptosystem without a trusted party,перенесенный на эллиптические кривые (в оригинальной статье используется мультипликативная группа конечного поля (поля Галуа)GF(p)). При этом есть ограничение:при любой жалобе (не сходится контрольная сумма) одного из участников на другого необходимо перезапустить процесс генерации с самого начала.

В нашей текущей реализации DKG используются стандартные эллиптические кривые seсp256k1 (Bitcoin, Ethereum) и функция хеширования SHA-256. Можно легко добавить, например, Ed25519 или даже российские кривые ТК-26 и хеш Стрибог, если потребуется. Также можно не завязываться на 256-битных кривых, а использовать 512-битные.

Участниками DKG у нас будут несколько экземпляров криптографических сервисов, выполняющих всю криптографическую магию. Чтобы не возникало вопросов о возможности непрозрачного общения между сервисами, единственным их интерфейсом будет взаимодействие с блокчейн-нодой. Один декрипт одна нода. Принципиально такое соотношение 1 к 1 не требуется, декриптов может быть больше или меньше чем нод, но выглядит логичным иметь одинаковый уровень децентрализации как на слое хранения/транспорта данных (см. раздел выше), так и на слое криптографии.

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

Протокол DKG Pedersen 91 на эллиптических кривых

Параметры протокола:

  1. Эллиптическая кривая E и генератор (Base) подгруппы этой кривой большого простого порядка q.

  2. Другой генератор (BaseCommit) той же подгруппы, для которого число x из соотношения BaseCommit = x * Base неизвестно никому.

  3. (k, n), гдеnобщее число развернутых криптографических сервисов (DecryptService), сгенерировавших пары ключей, аkминимальное число сервисов, которое необходимо для восстановления общего секрета. k <= (n+1)/2, то есть еслиk - 1участниковнечестные или у них украли ключи, то это никак не повлияет на безопасность общего секрета (MainPubliсKey).

Шаг 0. Индексы DecryptService

Каждому изnDecryptServiceприсваивается уникальный порядковый номер от 1 доn. Это нужно, потому что от порядкового номераDecryptServiceзависит коэффициент Лагранжа, который потребуется для реализации схемы K из N.

Шаг 1. Создание открытого ключа голосования

Каждый изnDecryptServiceгенерирует пару публичного (Pub_n)и приватного (priv_n) ключей для эллиптической кривой: j-йсервер генерирует пару ключей:priv_j,Pub_j,гдеPub_j = priv_j * Base(точка Base генератор простой подгруппы). И делает Pedersen commitment для публичного ключа:

  1. Генерируется случайное число, скалярr_j.

  2. Вычисляется точка, коммитС_j = r * BaseCommit + Pub_j.

  3. С_jпубликуется в блокчейн.

После того как каждый изnDecryptServiceопубликовал свой коммит ПедерсенаС_j, каждый DecryptService публикует свой скалярr_j. На основе опубликованных в блокчейне скаляров любой сторонний наблюдатель может восстановить публичные ключи каждого DecryptService Pub_j =С_j -r * BaseCommit а затем вычислить общий публичный ключ Pub (MainPublicKey) как сумму отдельныхPub_j.

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

Шаг 2. Генерация полиномов и раздача теней

Каждыйj-йDecryptService случайным образом:

  • Генерирует полином степениk - 1:f_j(x) = f_j_0 + f_j_1*x + ... + f_j_k-1* x^(k-1), где коэффициентf_j0 = priv_j, а остальные случайные элементы поляGF(q), гдеq порядок подгруппы точек.

  • Считает значения полинома для каждогоi-гоизnзначений:f_j(i) = f_j_0+ f_j_1*i + ... + f_j_k-1* i^(k-1). Значениеf_j(i)называется тенью (shadow).

  • Шифруетf_j(i)при помощиPub_iдля всех других серверов и публикует результаты шифрования. Таким образом,значениеf_j(i)может узнать только владелецpriv_i, т.е. DecryptService номерi.

Шаг 3. Проверка коэффициентов полиномов

Чтобы убедиться, что каждый из DecryptService следует протоколу DKG, они проверяют значения теней, полученных друг от друга. Каждый DecryptServiceпубликует каждый коэффициент своего полинома, умноженного на генератор Base: j-й сервер:fj,0* Base, fj,1* Base, ... , fj,k-1* Base, где fj,k-1 это коэффициент при степениk - 1.

После этого каждыйi-йDecryptServiceпроверяет все расшифрованные тениf_j(i)(гдеjиз множества от 1 доn, исключаяi), которые для него зашифровали другиеn - 1участников DKG. i-йDecryptServiceдля тени от сервераj:

  1. Вычисляетf_j(i) * Base

  2. Берет экспоненты его коэффициентов:fj,0* Base, fj.1* Base, ... , fj,k-1* Base

  3. Домножает каждый на соответствующую степеньi:fj,0* Base, i * ( fj,1* Base), ... , i^(k-1) * ( fj,k-1* Base)

  4. Складывает их.

Если результат сложения равенf_j(i) * Base(тень отjдляi, умноженная на генератор), то результат принимается. В противном случае публикуется жалоба на серверj: значение тениf_j(i), и протокол запускается с самого начала шага 0.

Если ни у кого нет жалоб, то каждый сервер вычисляет свой секретный ключs_iкак сумму значенийf_j(i)от всехjсерверов, включая себя.

Если взять любые изkучастников, то сложив ихs_i * Lagrange(k, i), где Lagrange(k, i) коэффициент Лагранжа, который зависит от номеров из выбранной группы (k) и номераi, мы получим приватный ключ, соответствующий общему ключу Pub (MainPublicKey), то есть по сути сумму всехpriv_i.

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

Мы решили проблему угона приватного ключа до начала голосования. Он не был создан централизовано, и чтобы получить доступ к данным голосования, злоумышленнику необходимо взломать K серверов, а не один. Но остается вопрос: как мы будем проводить расшифровывание. Будем ли мы собирать приватный ключ в этот момент и дадим ли шанс злоумышленнику? Пожалуй, не дадим.

Шаг 4. Распределенное дешифрование

Допустим, мы зашифровываем сообщение M на открытом ключе голосования (MainPublicKey):

  1. Генерируем число r и считаем R = r * Base.

  2. ВычисляемС = M + r *MainPublicKey.

  3. Получившийся шифротекст пара точек (R, C) мы публикуем в блокчейне.

  4. Владелец приватного ключаprivвычисляет значение:priv * R.

  5. И расшифровываетM:M = С -priv * R.

Таким образом, для расшифровывания (R, C) нужно вычислитьpriv * R.

Если наш приватный ключ распределен (допустим, что (k, n) = (3,6)), каждый криптографический сервис независимо считает значениеs_i * R, используя свою часть приватного ключа, и публикует результат в блокчейне. Назовем это значение частичной расшифровкой. Дальше остается домножить любые 3 из 6 результатовs_i * Rна соответствующий коэффициент Лагранжа, сложить три точки и получить priv * R. А используя это значение, мы расшифровываем сообщение М.

Таким образом, мы получили возможность расшифровывать результаты голосования, не храня и не собирая приватный ключ в одном месте. Мы проводим распределенное дешифрование, когда каждый из DecryptService вычисляет результат частичной расшифровки, публикует его, и на основании этих данных мы проводим итоговое дешифрование. При этом ни один из DecryptService не имеет возможности выполнить полное дешифрование единолично, а так как всё взаимодействие между криптографическими сервисами происходит в публичной области через блокчейн, мы всегда знаем, сколько раз выполнялись попытки дешифрования результатов.

Гомоморфное шифрование

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

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

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

Использовать такую возможность в голосовании мы можем, представив бюллетень в виде матрицы, где каждая строка это отдельный вопрос, по которому принимается решение, а ячейки в пределах строки это варианты ответов на этот вопрос. Заполняя бюллетень, участник голосования выбирает вариант, и мы обозначаем его единицей. Остальные ячейки заполняются нулями. Затем мы шифруем каждую ячейку бюллетеня. Именно в таком зашифрованном виде бюллетень попадает в блокчейн. Что именно зашифровано в каждой ячейке мы не знаем.

Бюллетень в виде матрицы вопросов и вариантов ответовБюллетень в виде матрицы вопросов и вариантов ответов

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

Подсчет результатов в зашифрованном видеПодсчет результатов в зашифрованном виде

В итоге мы получаем не только совместную и прозрачную работу нескольких независимых сервисов при вычислении итогов голосования, где ни один из них не может эти результаты подтасовать. Мы также добились очень высокого уровня конфиденциальности голосования. Мы анонимизировали выбор участников голосования через сложение столбиком у нас есть сумма, но нам неизвестны слагаемые!

Чтобы добиться этого, мы используем криптосистему Эль-Гамаля.Исходно эта криптосистема опирается на задачу дискретного логарифма на конечном поле и является гомоморфной к умножению, но она может быть модифицированадля работы на эллиптических кривыхи приведена к гомоморфизму по сложению. В разделе выше показано, как выглядит зашифрованное сообщение:

Зашифрованное сообщение 1: ( R1, С1 ) =( r1 * Base,M1 + r1 *MainPublicKey)

Зашифрованное сообщение 2: ( R2, С2 ) =( r2 * Base,M2 + r2 *MainPublicKey)

Их сумма: ( R1 + R2, C1 + C2 ) = ( ( r1+r2 ) * Base, M1 + M2 + ( r1 + r2 ) *MainPublicKey)

Сумму расшифровываем так же, как отдельные сообщения (помним чтоMainPublicKey= priv * Base):

( M1 + M2 ) = ( C1 + C2 ) priv * ( R1 + R2 ) = M1 + M2 + ( r1 + r2 ) *MainPublicKey priv * ( r1 + r2 ) * Base = M1 + M2

Кто-то скажет магия, кто-то возразит математика.

В результате мы, кажется, сделали все возможное, чтобы защитить участника голосования. Его голос нельзя подделать, его нельзя прочитать, его практически нельзя заблокировать или потерять. Участника голосования нельзя лишить премии за выбор неправильного кандидата.

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

Доказательства с нулевым разглашением (ZKP Zero Knowledge Proofs)

Однако не только организатор или внешний злоумышленник могут иметь мотивацию повлиять на исход выборов. Нарушитель может затесаться и в ряды участников голосования. Поскольку мы используем очень простую механику сложения столбиком и не заглядываем в индивидуальные бюллетени, у участника голосования есть возможность покопаться в клиентском приложении и сформировать бюллетень, где он вместо "1" проставит за своего кандидата значение 100500. Или 100500 за того, кого недолюбливает. Система посчитает итоги, и кандидат нарушителя победит с внушительным преимуществом.

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

Одна из самых наглядных демонстраций работы ZKP (интерактивной разновидности) это Пещера Али-Бабы или Лабиринт:

Участник A обладает ключом, открывающим дверь в лабиринте, и хочет доказать это участнику B, но не хочет показывать ключ. Чтобы В мог убедиться в верности утверждения А, они организуют серию испытаний:

  1. А заходит в лабиринт пока В отвернулся. В не знает, в какую сторону пошел А.

  2. В дает А указание выйти с какой-либо стороны, например, слева.

  3. Если А действительно обладает ключом, он может появиться с любой стороны и выполняет указание В.

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

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

ZKP на бюллетене

По каждому вопросу мы, помимо заполненных и зашифрованных ячеек с вариантами выбора, к каждой ячейке приложим NIZK, доказывающее, что в этой ячейке зашифровано одно из значений 0 или 1 но не раскрывающее, какое именно. И дополнительно приложим доказательство, что зашифрованная сумма значений всех ячеек по вопросу равна 1. Это означает, что участник голосования может поставить 1 в любую из ячеек и выбрать только один вариант.

При желании (а оно у нас есть) мы можем на базе этой схемы ZKP реализовать более сложные схемы голосований. Например, взвешенное голосование, где каждый участник отдает не один голос, а количество голосов, пропорциональное своей доле акций компании. Для этого мы должны вместо 1 создать ZKP для значения веса голоса участника. Или вариант голосования с множественным выбором, где каждый голосующий может выбрать не один вариант из N, а несколько. Для этого мы по каждой ячейке добавляем ZKP для ряда значений [0, 1, 2, 3]. Суммарный ZKP может быть на значение [3] тогда голосующий должен распределить все свои голоса. Или на ряд значений[1, 2, 3] то есть он может выбрать от 1 до 3 вариантов, но не может не ответить на вопрос.

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

Структура зашифрованного бюллетеня выглядит следующим образом:

(R_1, C_1), Proof_1,

.........................

(R_M, C_M), Proof_M,

Sum_Proof

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

ZKP на частичных расшифровках

Хорошо, у нас есть список бюллетеней, подписанных и отправленных валидными участниками голосования, эти бюллетени правильно заполнены, они зашифрованы, и мы не знаем, кто и как проголосовал. Настал момент, когда мы должны получить результаты голосования. Использовав свойство гомоморфности,мы получили зашифрованные результаты суммарный бюллетень. Этот суммарный бюллетень получен независимо каждым из наших криптографическихсервисов,и они должны провести процедуру распределенного расшифрования.

Первое очевидное условие: каждый декрипт должен получить такой же итоговый бюллетень,как и все остальные,иначе магия распределенного расшифрования не сработает. Но это не выглядит большой проблемой, когда у нас есть общий публичный список бюллетеней в блокчейне и именно он является источником данных для всех криптосервисов.

Второе условие: если расшифровывание нескладывается,и мы подозреваем, что некоторые криптографические сервисы решили саботировать голосование, неплохо бы иметь возможность проверить, какой именно из сервисов сбоит. Для этого при публикации частичных расшифровок каждый криптосервис создает и прикладывает ZK-доказательство расшифровки, используя алгоритмZKP Chaum-Pedersen, который доказывает знание числа x для двух соотношений A = x * B и C = x * D (где A B, C, D точки на одной кривой).

Теперь у любого квалифицированного стороннего наблюдателя есть возможность:

  • самостоятельно провести гомоморфное сложение валидных бюллетеней и получить итоговый бюллетень;

  • проверить доказательства расшифровки этого суммарного бюллетеня от каждого криптографического сервиса;

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

Для удобства последний шаг мы проведем сами и зафиксируем итоги голосования в блокчейне как массива массивов вида [ [ 2,5,6 ], [ 3,5,5 ], [ 7,6 ], [ 10,3 ] ].

Смарт-контракты

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

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

Именно такую роль выполняют смарт-контракты в блокчейне. Это умный ящик для голосования, который фиксируетправила голосования,хранит список участников и статусную модель голосования: какой публичный ключ принадлежит участнику, какой криптографическому сервису. Также смарт-контракт регистрирует публичные данные протокола DKG, проверяет и сохраняет отправленные голоса и результаты голосования.

А что дальше?

К сожалению, нельзя все операции системы возложить на смарт-контракт.Во-первых,он может оперировать только открытыми данными никаких приватных ключей и паролей, а именно они участвуют в процессе DKG. Во-вторых, это все-таки распределенные, а значит дорогие и медленные вычисления. Не всю тяжелую математику можно положить в смарт-контракт. Нотак или иначе,это является одним из направлений развития системы оптимизация криптографических библиотек и возможность более полной автоматизации процесса голосования.

Еще одно важное для нас направление развития то, о чем разработчики, увлеченные красивым решением технической проблемы, склонны забывать. А именно UX опыт пользователя :)Прикрутить блокчейн это современно, модно,и в данном случае даже целесообразно, но попробуйте пользователю, который привык к UX современных мобильных и веб-приложений, объяснить, что такое его персональный приватный ключ и что без него он не сможет сделать в системе ровным счетом ничего.Или дать ему одновременно гарантию, что отправленная им транзакция будет принята системой, не заставляя ждать его, пока мы сами в этом убедимся. Нода майнит, прелоадер крутится, пользователь ждет и не понимает чего именно: Ну не знаю, почту я отправляю, все работает мгновенно, а тут чего-то ждать надо?. Поэтому мы активно работаем над тем, чтобы система сохраняла привычные пользовательские свойства, не теряя своих технологических преимуществ.

Также не стоитзабывать о том, чтолюбое техническое решениедолжны закрывать реальные потребности и боли пользователей из реального мира.Протокол блокчейн-голосования в вакуумеможет быть интересен исследователям и разработчикам, но не бизнесу. Бизнес хочет эффективного решения своих задач. Для этого голосование должно не только иметь необходимые технические характеристики, но также соответствоватьустановившимсябизнес-процессам принятия решений, удовлетворять требованиям законодательного регулирования и т.д.

Каждое из этих направлений развития это множество нетривиальных и разноплановых задач, которые совершенно точно не дают нам скучать.Надеюсь, вам было интересно. Буду рад ответить в комментариях на вопросы по статье.

Подробнее..

Транспортный протокол QUIC приняли в качестве стандарта RFC 9000

01.06.2021 02:22:59 | Автор: admin


QUIC новый транспортный протокол связи, который отличается уменьшенным временем задержки, большей надёжностью и безопасностью, чем широко используемый сегодня TCP (RFC 793).

Уже много рассказывалось о преимуществах транспорта QUIC, который взят за основу будущего стандарта HTTP/3. В HTTP следующего поколения транспорт TCP меняется на QUIC, что означает автоматическое ускорение соединений и зашифровку всего интернет-трафика, который раньше шёл в открытом виде по TCP. Нешифрованный QUIC не предусмотрен вообще.

В мае 2021 года состоялось знаменательное событие: протокол QUIC принят в качестве официального стандарта RFC9000. Это великолепные новости для всей интернет-экосистемы.

Утверждением таких стандартов занимается Инженерный совет Интернета (IETF). Ранее были оформлены вспомогательные стандарты RFC 9001, RFC 9002 и RFC 8999.

Таким образом, QUIC версии 1 теперь официально принят и утверждён. Все участвующие стороны могут завершить эксперименты с черновиками протокола и перейти на первую официальную версию.

В последние годы QUIC был одним из главных приоритетов IETF. Появившись как эксперимент Google, вскоре разработка QUIC вышла на международный уровень. Она велась почти пять лет. Зафиксировано 26 очных собраний, 1749 задач в трекере и многие тысячи писем в почтовой рассылке.

QUIC очень амбициозный проект, который принесёт большие изменения. Транспортная экосистема интернета за несколько десятилетий закостенела, а QUIC оживит её, пишут инженеры из компании Fastly, которые входят в рабочую группу по разработке протокола.

Окостенение означает, что система с каждым годом становится всё менее гибкой, менее подвижной. QUIC принесёт в транспортный уровень множество инноваций, включая обязательное шифрование, версионность, гораздо более богатый и более производительный набор сервисов, поверх которых будут строиться новые технологии. Предполагается, что QUIC приведёт к появлению нового поколения интернет-инноваций. Это уже начало происходит с расширениями, такими как ненадёжные датаграммы (Unreliable Datagram Extension). Ненадёжные датаграммы открывают двери перед новым классом медиа в реальном времени и другими приложениями, которым нужен более функциональный транспорт, чем обязательная доставка пакетов с обрывом канала при потере нескольких пикселей. Мы уже видим многообещающие технологии, такие как MASQUE и WebTransport.

HTTP/3


Стандарт HTTP/3 (это HTTP поверх QUIC) идёт с небольшим опозданием за QUIC и тоже будет официально принят в самое ближайшее время.


34-й (!) драфт HTTP/3

С момента принятия HTTP/2 прошло шесть лет: спецификация RFC 7540 опубликована в мае 2015-го, но пока не используется повсеместно. Протокол реализован во всех браузерах ещё с конца 2015 года, а спустя три года только 45,4% из 10 млн самых популярных интернет-сайтов поддерживают HTTP/2. Два с половиной года назад таких было 31,2%. Севсем недавно на HTTP/2 перешли сайты Amazon, Paypal, Telegram.org.

Cейчас практически готова третья версия HTTP/3, осталось совсем немного подождать.

QUIC представляет собой замену TCP, которая работает поверх UDP. Изначально эта технология была создана инженерами Google, как и предыдущий протокол SPDY, который стал основой HTTP/2. В первое время QUIC именовали HTTP/2-encrypted-over-UDP.

Затем разработку QUIC передали в IETF для стандартизации. Здесь он разделилcя на две части: транспорт и HTTP. Идея в том, что транспортный протокол можно использовать также для передачи других данных, а не только эксклюзивно для HTTP или HTTP-подобных протоколов. Однако название осталось таким же: QUIC. Разработкой транспортного протокола занимается рабочая группа QUIC Working Group в IETF.

Долгое время версия IETF называлась iQUIC, в то время как Google и другие продолжили работу над собственной реализацией gQUIC, но 7 ноября 2018 года один из ведущих разработчиков протокола Дмитрий Тихонов объявил, что стороны достигли совместимости протоколов, и теперь разработка продолжится в общем русле. QUIC в Chrome включается в настройках chrome://flags. Есть ещё расширение-индикатор, которое показывает, какие сайты поддерживают QUIC.



Встроенная безопасность и производительность


В чём преимущества транспортного протокола QUIC перед TCP? Преимуществ очень много. По словам руководителя рабочей группы Марка Ноттингема, переход от устаревшего TCP на новые протоколы просто неизбежен, поскольку сейчас очевидно, что TCP страдает от проблем неэффективности.

Поскольку TCP протокол доставки пакетов по порядку, то потеря одного пакета может помешать доставке приложению последующих пакетов из буфера. В мультиплексированном протоколе это может привести к большой потере производительности, объясняет Марк Ноттингем. QUIC пытается решить эту проблему с помощью эффективной перестройки семантики TCP (вместе с некоторыми аспектами потоковой модели HTTP/2) поверх UDP.

Кроме перехода значительного объёма трафика с TCP на UDP, протокол QUIC требует обязательного шифрования: нешифрованного QUIC не существует вообще. QUIC использует TLS 1.3 для установки ключей сессии, а затем шифрования каждого пакета. Но поскольку он основан на UDP, значительная часть информации о сессии и метаданных, открытых в TCP, шифруется в QUIC.



В статье Будущее интернет-протоколов Марк Ноттингем говорит о значительных улучшениях в безопасности с переходом на QUIC:

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

Кроме того, становится невозможна пассивная оценка RTT и потерь пакетов путём простого наблюдения за соединением; там недостаточно информации для этого.

Отсутствие наблюдаемости вызвало серьёзную озабоченность у некоторых представителей сообщества операторов связи. Они говорят, что такие пассивные измерения критически важны для отладки и анализа их сетей.

Одно из предложений для решения этой проблемы введение спин-бита. Это бит в заголовке, который меняет своё значение один раз по пути туда-обратно, так что наблюдатель может оценить RTT. Поскольку он не привязан к состоянию приложения, то не должен выдавать никакой информации о конечных точках, кроме примерной оценки местоположения сети.

Возможно, принятие стандарта QUIC произошло бы и раньше, если бы компания Google не поспешила внедрить свою реализацию в браузер Chrome, так что случилось раздвоение стандарта.

Тем не менее, прогресс неизбежен и в ближайшие годы обязательно продолжится стандартизация и повсеместное внедрение различных протоколов нового поколения, в том числе HTTP/3 на транспорте QUIC.



См. также:





Отмечайте юбилей GlobalSign и получайте скидки!


Подробнее..

Даешь свободную литературу! Или как я с политикой вуза боролся

25.04.2021 12:10:20 | Автор: admin

Доброго времени суток, хабровчане! Это мой первый пост на форуме, так что прошу строго не судить.

Коротко обо мне: студент, увлекаюсь электроникой, микроконтроллерами, и программированием. Однако, моя специальность ни коим образом не связана с It. Со мной покончено, переходим к сути.

Как и полагается любому техническому вузу в нашем есть куча интернет ресурсов, которыми вуз чрезмерно гордится. Однако есть оборотная сторона медали качество этих сервисов. А именно, если говорить про электронную библиотеку, о коей и пойдет речь в данной статье, то в ней напрочь отсутствует возможность скачивания pdf-версии нужной тебе методички, точнее она есть, но за это придется заплатить немало денЯк. Деньги далеко не маленькие (если говорить именно про цену за вузовские методички). Если же такой формат не устраивает, то можешь пользоваться онлайн библиотекой.

В онлайн библиотеке есть просмотрщик книг, через который можно читать литературу.

Просмотрщик оформлен максимально неудобно: долгое время не работал переход на определенную страницу книги, и книгу в 700 страниц приходилось перелистывать по страничке, что превращалось в адскую муку. Но самое ужасное в этом сайте то, что каждые 20 минут он просит авторизоваться по новой

И теперь представьте картину: человек пытается подготовиться к контрольной по квантовой механике по методичкам преподавателя, объемом 700 страниц, где необходимый материал находится на 500, и может перелистывать по 5 страничек в минуту, и каждые 20 минут, его попытки приходится возобновлять. В общем, жесть. И вот после очередной неудачной попытки прочитать нужную главу, я решил, что пришло время положить конец данному произволу.

Первым делом, средствами браузера, была получена ссылка на необходимый ресурс с изображением страницы. Ссылка выглядела примерно следующим образом:

http://www.<название сайта>/plugins/<название просмоторщика>/getDoc.php?Id=<id книги>&page=<номер страницы>

После получения данной ссылки скачивание книги не составляет и труда:

  1. Необходимо получить id книги (или лучше ссылку на нее)

  2. Узнать количество страниц

  3. В самом простом цикле for пробежаться по всем страницам, загрузив их на компьютер

  4. Объединить фотографии в единый файл pdf

  5. Радоваться

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

Самая первая версия программы выглядела максимально убого: я использовал java и библиотеку selenium для работы с сетью. Приложение получилось явно не user-friendly: запускалась только из IDEA, в которой ручками необходимо было вставлять ссылку на каждую книгу, также ручками забивать количество страниц. Более того, самым убожеством был тот факт, что приложение полностью имитировало пользователя:

  • Открывался сайт в браузере

  • Далее проводился поиск полей для логина и пароля и их заполнение

  • Затем переход по ссылке с первой страницей книги

  • И сочетание клавиш CTRL+S, нажатие на Enter.

В общем фу! Нельзя так делать!

Но когда у тебя на носу контрольная по квантмеху, и так сойдет. Программа была написана минут за 20, и книжки она выкачивала долго, и иногда не все страницы, хуже того, после скачивания, опять же ручками приходилось объединять картинки в единый файл pdf.

Но, не спешите бросаться помидорами, данный кошмар в дальнейшем был преобразован в весьма неплохой код. Я перешел на Delphi! Да, многие могут сказать, что язык устарел, и не обладает должным функционалам, однако в своей работе я его применяю постоянно. Обучился сему творению благодаря Михаилу Фленову (низкий поклон вам, Миша).

Итак, переходим к финальной версии программы, которая была написана, спустя месяца два три после моего первого чудо творения.

Рисунок 1 - Главная форма приложенияРисунок 1 - Главная форма приложения

Итак, суть осталось точно такой же, да и шаги не поменялись. Программулиной Wire Shark узнал, какая последовательность отправляется в POST запросе при авторизации. И меня сильно напугал один факт: пароль был зашифрованным.

Небольшое отклонение: логин на сайте представляет собой некоторый набор цифр, а пароль имя, написанной на русском языке. Однако, в программе WireShark на сайт отправлялся шестнадцатеричный код следующего формата: %D0% FF %D0% FF %D1% FF. Как позже выяснилось, символы FF кодировали буквы имени, а %D0% и %D1%, что то вроде контрольных байт. З.. Огромная просьба к знатокам, читающим эту статью, если это какой либо общеизвестный сетевой протокол, то не кидайтесь помидорами, а дайте, пожалуйста, ссылку, где можно прочитать про него. Но т.к. я не сталкивался с таким раньше, пришлось разбираться во всем самому.

Первым делом я обратил внимание на повторяющиеся символы %D1% и %D0%, которые, как я сразу и предположил, являются контрольными байтами, однако, оставалась загадкой, чем отличается %D0% от %D1%. Но это пока отложим, едем дальше. Как же шифруется пароль? А он шифруется достаточно просто, предположим, меня зовут Василий, давайте запишем мое имя в ACII символах:

82 (В) A0 (а) E1 (с) A8 (и) AB (л) A8 (и) A9 (й)

А на сайт отправляется следующая последовательность:

72 (В) 90 (а) 61 (с) 98 (и) 9B (л) 98 (и) 99 (й)

Хм, просматривается одна зависимость. А, точно! Если внимательно приглядеться, то у каждого символа, отправляемого на сайт, старший байт уменьшается на 1. Бум! Загадка разгадана.

Хотя, стоп, а почему же тогда вместо D1 (буква с), на сайт отправляется 61? А черт ее знает! Просто запомним, что для символов, лежащих в диапазоне от E0 до EF из старшего байта необходимо вычитать не 1, а 6. Собственно и все! А помните, я говорил про контрольный символ %D1%? Так вот, этот символ как раз таки ставится перед символами из данного диапазона. Ну, собственно и все. Далее привожу кусок кода, отвечающий за шифровку.

Код ""
 for i := 1 to length(password) do begin    temp := Ord(password[i]);    //Представили пароль в HEX виде    if (temp < 1088) or (temp > 1103) then // Значения символов E0 и EF        begin                 //Если вне этого диапазона, то %D0%pasBytes[i] := '%D0%' + IntToHex(((temp) - 896), 2); // + 128 - 1024 для шифрования   newPassword := newPassword + pasBytes[i];         end    elsebegin   //Иначе %D1%pasBytes[i] := '%D1%' + IntToHex(((temp) - 960), 2); // +64 -1024 для шифрования                   newPassword := newPassword + pasBytes[i];end; end;//1024 здесь вычитается потому, что в Delphi почему то ASCII символы начинаются с #400//Почему - не ясно 

Финальная последовательность, отправляемая на сайт, будет выглядеть следующим образом:

%D0% 72 %D0% 90 %D1% 61 %D0% 98 %D0% 9B %D0% 98 %D0% 99

Собственно это и была самая сложная часть работы.

Далее, после авторизации, пользователь вставляет в строку ввода ссылку на необходимую книгу. Программа же в это время отправляет Get запрос на сервер, из ответа которого, затем в автоматическом режиме находит название книги, ее ID, и количество страниц. Все эти данные сохраняются в глобальные переменные. И, затем, после нажатия кнопки Download, в отдельном потоке отправляются Get запросы на сервер, из которых и забираются картинки с изображениями страниц. Которые в дальнейшем, с помощью библиотеки Synapse формируются в единый PDF файл.

На этом работы программы и заканчивается. К приложению добавлены всякие украшательства, по типу значка и строчки загрузки.

В общем, то и все. Если кому надо, могу выложить исходники на Github и выложить в комментариях ссылку. Также хотелось услышать ваше мнение по поводу моей дешифровки. Всем спасибо, до скорых встреч на просторах интернета.

Подробнее..

Шифрование сообщений в Python. От простого к сложному. Шифр Цезаря

13.04.2021 22:15:19 | Автор: admin

Немного о проекте

Мне, лично, давно была интересна тема шифрования информации, однако, каждый раз погрузившись в эту тему, я осознавал насколько это сложно и понял, что лучше начать с чего-то более простого. Я, лично, планирую написать некоторое количество статей на эту тему, в которых я покажу вам различные алгоритмы шифрования и их реализацию в Python, продемонстрирую и разберу свой проект, созданный в этом направлении. Итак, начнем.


Для начала, я бы хотел рассказать вам какие уже известные алгоритмы мы рассмотрим, в моих статьях. Список вам представлен ниже:

  • Шифр Цезаря

  • Шифр Виженера

  • Шифр замены

  • Омофонический шифр

  • RSA шифрование

Шифр Цезаря

Итак, после небольшого введения в цикл, я предлагаю все-таки перейти к основной теме сегодняшней статьи, а именно к Шифру Цезаря.

Что это такое?

Шифр Цезаря - это простой тип подстановочного шифра, где каждая буква обычного текста заменяется буквой с фиксированным числом позиций вниз по алфавиту. Принцип его действия можно увидеть в следующей иллюстрации:

Какими особенностями он обладает?

У Шифра Цезаря, как у алгоритма шифрования, я могу выделить две основные особенности. Первая особенность - это простота и доступность метода шифрования, который, возможно поможет вам погрузится в эту тему, вторая особенность - это, собственно говоря, сам метод шифрования.

Программная реализация

В интернете существует огромное множество уроков, связанных с криптографией в питоне, однако, я написал максимально простой и интуитивно понятный код, структуру которого я вам продемонстрирую.

Начнем, пожалуй, с создания алфавита. Для этого вы можете скопировать приведенную ниже строку или написать все руками.

alfavit =  'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ'# Создаем алфавит

Далее, нам нужно обозначить программе шаг, то есть смещение при шифровании. Так, например, если мы напишем букву "а" в сообщении, тот при шаге "2", программа выведет нам букву "в".

Итак, создаем переменную smeshenie, которая будет вручную задаваться пользователем, и message, куда будет помещаться наше сообщение, и, с помощью метода upper(), возводим все символы в нашем сообщении в верхний регистр, чтобы у нас не было ошибок. Потом создаем просто пустую переменную itog, куда мы буем выводить зашифрованное сообщение. Для этого пишем следующее:

smeshenie = int(input('Шаг шифровки: '))    #Создаем переменную с шагом шифровкиmessage = input("Сообщение для шифровки: ").upper()    #создаем переменнную, куда запишем наше сообщениеitog = ''    #создаем переменную для вывода итогового сообщения

Итак, теперь переходим к самому алгоритму шифровки. Первым делом создаем циклfor, где мы определим место букв, задействованных в сообщении, в нашем списке alfavit, после чего определяем их новые места (далее я постараюсь насытить код с пояснениями):

for i in message:    mesto = alfavit.find(i)    #Вычисляем места символов в списке    new_mesto = mesto + smeshenie    #Сдвигаем символы на указанный в переменной smeshenie шаг

Далее, мы создаем внутри нашего цикла условие if , в нем мы записываем в список itog мы записываем наше сообщение уже в зашифрованном виде и выводим его:

if i in alfavit:        itog += alfavit[new_mesto] # здесь мы прибавляем значение правого операнда к левому    else: # и присваиваем эту сумму левому операнду.        itog += iprint (itog)

Итоговый вид программы

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

alfavit =  'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ'smeshenie = int(input('Шаг шифровки: '))message = input("Сообщение для ДЕшифровки: ").upper()itog = ''for i in message:    mesto = alfavit.find(i)    new_mesto = mesto + smeshenie    if i in alfavit:        itog += alfavit[new_mesto]    else:        itog += iprint (itog)

Дешифровка сообщения

Возможно это прозвучит несколько смешно, но мы смогли только зашифровать сообщение, а насчет его дешифровки мы особо не задумывались, но теперь дело дошло и до неё.

По сути, дешифровка - это алгоритм обратный шифровке. Давайте немного переделаем наш код (итоговый вид вы можете увидеть выше).

Для начала, я предлагаю сделать "косметическую" часть нашей переделки. Для этого перемещаемся в самое начало кода:

alfavit =  'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ'smeshenie = int(input('Шаг шифровки: '))message = input("Сообщение для ДЕшифровки: ").upper()    #заменяем слово шифровка, на дешифровкаitog = ''

Остальное можно оставить так же, но если у вас есть желание, то можете поменять названия переменных.

По большому счету, самые 'большие' изменения у нас произойдут в той части кода, где у нас находится алгоритм.

Итог

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

Подробнее..

Удобное шифрование с удостоверяющим центром

25.02.2021 12:15:55 | Автор: admin

Привет всем читателям Хабра! В этой статье рассмотрим проблемы и способы использования шифрования в документообороте. Перед командой стояла задача разработки новой архитектуры шифрования, которую нужно внедрить в наш продукт.

Наш продукт

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

Первая реализация шифрования

Изначально, когда разрабатывался продукт, для шифрования использовался симметричный алгоритм AES с использованием ключа. Данный способ не является удобным, так как нужно было всегда вводить ключ для получения доступа к документу. Также все сотрудники, которые имеют доступ к данным, должны были знать ключ для расшифровки. Такая реализация выглядит не очень удобно и небезопасно. Скорее всего, для простоты пользователи начнут использовать один ключ для шифрования всех документов, а чтобы не забыть - напишут его на стикере и приклеят к монитору. Жестко регулировать действия пользователя в данном случае - не лучшее решение. Можно заставить пользователей для каждого документа использовать уникальный ключ и с течением времени его изменять. Но тогда, при передаче документа другому пользователю, нужно будет передавать и ключ, и, наверняка, кто-нибудь при очередном обновлении ключа забудет его, и доступ к данным будет утерян. Разработка новой архитектуры, в которой учитывались бы все минусы предыдущей, напрашивалась сама собой.

Новая архитектура

Главной задачей для новой архитектуры был отказ от необходимости ввода ключа при расшифровке данных пользователями. Также имело место "пожелание" шифровать, используя сертификаты в новой версии от управляющего центра. Применение сертификата для шифрования означало использование ассиметричных алгоритмов с открытым и закрытым ключом, что немного не соответствовало концепции нашего продукта. Симметричный алгоритм позволяет расшифровывать данные любым пользователем, который знает ключ. Ассиметричный алгоритм же, в свою очередь, подразумевает, что расшифровать данные может только пользователь, имеющий парный закрытый ключ открытому ключу, которым, собственно, и были зашифрованы данные, что в корне нам не подходило. Документ, зашифрованный одним пользователем, должен был быть беспрепятственно расшифрован другим пользователем, который имел бы право доступа к данным.

Вы предложите решение, Вы же эксперт...

Асимметричное шифрование - это алгоритм шифрования данных, который использует уникальную пару ключей (открытый и закрытый) таким образом, что для каждого открытого ключа есть лишь один закрытый. В нашей системе, где есть сервер и множество пользователей, такая пара ключей будет у каждого. Закрытые ключи хранятся у пользователей в специальных криптоконтейнерах, к которым привязан сертификат. Сертификат, помимо всего прочего, содержит открытый ключ, и передача сертификата как раз означает передачу открытого ключа. Переходя к нашей системе - использование данного алгоритма означает, что сертификаты каждого пользователя (а точнее их открытые ключи) необходимо передать всем пользователям между собой (что уже кажется не самой лучшей идеей, если, например, таких пользователей тысячи и разбросаны они по разным концам света). Самое главное противоречие, с которым мы столкнулись - это как зашифровать данные так, чтобы расшифровать их мог любой пользователь, имеющий определенный доступ. Например, чтобы расшифровать документ пользователю X, нужно, чтобы этот документ был зашифрован именно его открытым ключом. Были озвучены самые разные варианты, порой даже безумные - можно шифровать документ в нескольких экземплярах для каждого пользователя ("самая лучшая идея", исходя из того, что пользователей может быть много).

Все размышления по этому поводу сводились к тому, что если мы не можем знать, кто будет расшифровывать документ, то и непонятно, каким открытым ключом его шифровать. Одна из первых идей, которая пришла в голову - это использовать сервер в нашей архитектуре. Каждый клиент при установке нашего продукта регистрируется на сервере, таким образом может получить от сервера его сертификат (открытый ключ) и все документы шифровать им. Далее, после передачи документа другому пользователю, тот для расшифровки документа должен был бы отправить документ на сервер, который его расшифрует и передаст обратно пользователю. Помимо основного недостатка, а именно, передача документа в открытом виде, данная схема имеет ряд других критичных недостатков, главный из которых - передача документа по сети. Документы бывают разные, и двойная передача его на сервер, а потом обратно - не самая удачная мысль. И второе - а что будет, если сервер вдруг упадет или будет недоступен, доступ к данным будет невозможен. Если первый недостаток можно было обойти, например, шифруя документ открытым ключом пользователя, который запрашивает расшифровку, то два других недостатка избежать невозможно.

Эврика!

Внимательное изучение протокола HTTPS (или SSL) показывает интересную особенность, а именно то, что хоть протокол и использует асимметричное шифрование, на самом деле применяется этот алгоритм лишь для: "выработки сессионного ключа, который, в свою очередь, используется более быстрыми алгоритмами симметричной криптографии для шифрования большого объёма данных" (http://personeltest.ru/aways/ru.wikipedia.org/wiki/SSL). Таким образом можно сказать, что 2 клиента между собой при помощи асимметричного алгоритма шифрования договариваются о симметричном ключе, который и используют в дальнейшем для симметричного шифрования данных при передаче. Это сделано для увеличения скорости работы, ведь асимметричные алгоритмы работают намного медленнее, чем симметричные. Сама идея генерации симметричного ключа в нашем случае может выглядеть так, что данные будут шифроваться все также, используя симметричное шифрование одним ключом, который сгенерирован сервером. А вот сам ключ нужно будет зашифровать, используя асимметричный алгоритм и открытый ключ пользователя, которому затем и передаст сервер этот зашифрованный ключ (каждому свой соответственно). Пользователи, получив от сервера зашифрованный ключ, расшифровывают его своим закрытым ключом и используют его для расшифровки и зашифровки данных. Таким образом мы получаем, что ключ, который используется в симметричном шифровании данных, вводит не сам пользователь, а его генерирует сервер, для каждой метки свой уникальный. Далее пользователь при запросе сервера о доступных ему метках получает вместе с ними и зашифрованный ключ. Также была решена и проблема с недоступностью сервера, все полученные данные от сервера хранятся у пользователей, в том числе и зашифрованный ключ. Соответственно, при отсутствии связи с сервером, ключ берется из хранилища, и, тем самым, доступ к зашифрованным данным не пропадает.

Итог

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

P.S.

Надеюсь этот пост кому-нибудь пригодится и поможет сэкономить время.

Подробнее..

Улучшаем Кузнечик на Rust

03.05.2021 20:08:12 | Автор: admin

Начало

Порой, простое и красивое решение лучше правильного и выдержанного. Возможно, именно так бы и было, если в жизни всё происходило как в учебниках. На практике же, нужно пройти долгий путь, чтобы создать задуманное.

Отступая от лирики, в данной статье я хочу рассказать заинтересованному читателю про шифрование по ГОСТ, а именно алгоритм Кузнечик, и про то, что стоит обратить внимание на новые и перспективные средства язык Rust.

Хватит отступлений, предлагаю начать!

Что будет в данной статье

В данной статье я хочу рассказать вам о моей реализации алгоритма Кузнечик на языке Rust. О том почему я выбрал данный язык. И что получилось в итоге. К сожалению, материала получилось многовато, поэтому некоторые главы я оставил на потом.

Для тех, кто уже устал читать, даю ссылочку на код, возможно кому-то пригодится.

Rust или не Rust?

Rust или C++, Go или Rust, Rust или , на эту тему существует уже довольно много статей, поэтому я ограничусь лишь парочкой слов. Rust сочетает в себе все современные и необходимые для разработки средства, включая многочисленные плюшки, которые нравятся разработчикам. Являясь при этом системным языком, Rust не обладает дополнительным оверхедом в виде сборщика мусора и выполняет максимум оптимизаций на этапе компиляции, а также позволяет писать нативный код для встраиваемых систем.

Я считаю, для данной задачи Rust удовлетворяет требованиям производительности и использования памяти, а также что, он более перспективный и удобный в использовании, чем тот же C++. Это лишь моё субъективное мнение и никого не прошу придерживаться его. К тому же, язык программирования это всего лишь инструмент со своими особенностями, преимуществами и недостатками. Если вы считаете, что это не так рад буду услышать вашу позицию по данному вопросу.

Реализация

Перед тем, как вдаваться в подробности кода было бы не плохо продемонстрировать HelloWorld. Для начала подключим библиотеку kuznechik:

Cargo.toml:

[dependencies]kuznechik = "0.2.0"

Теперь зашифруем и расшифруем строку "Hello World!":

main.rs:

fn hello_world() {    // Инициализация    let gamma = vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xce, 0xf0, 0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf0, 0x01, 0x12,                     0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x90, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19];    let kuz = Kuznechik::new("Кузнечик на Rust").unwrap();    let mut alg = AlgOfb::new(&kuz);    alg.gamma = gamma.clone();        let data = String::from("Hello World!").into_bytes();    // Шифрование    let enc_data = alg.encrypt(data.clone());    println!("Encrypted data:\n{:?}", &enc_data);    // Расшифрование    alg.gamma = gamma;    let dec_data = alg.decrypt(enc_data);    println!(        "Decrypted data:\n{}",        String::from_utf8(dec_data.clone()).unwrap()    );    assert_eq!(dec_data, data);}

В коде, представленном выше для создания объекта Kuznechik используется пароль в виде строки. Функция Kuznechik::new() принимает строку и хеширует её по алгоритму Sha3-256 для получения мастер ключа. Также вы можете напрямую задать мастер ключ, для этого можно воспользоваться функцией Kuznechik::new_with_master_key(). Данная функция принимает массив длиной 256 бит (тип [u8; 32]).

Функции alg.encrypt() и alg.decrypt() принимают на вход и возвращают вектор байтов Vec<u8>. Данные функции принимают во владение входные вектора. В некоторых режимах длина выходного вектора больше входного, связано это с процедурой дополнения.

Обзор библиотеки

Архитектура

Архитектура библиотеки устроена так, что неизменяемые данные ключи, хранятся в структуре Kuznechik, а изменяемые и присущи конкретному режиму шифрования данные в структурах, начинающихся с префикса Alg- (в данном примере - AlgOfb). Это касается и функционального кода. Всё это позволяет создать один экземпляр Kuznechik (единожды развернуть ключи) и использовать разные режимы.

Режимы шифрования

Алгоритм Кузнечик может работать в шести режимах (типы AlgEcb, AlgCtr, AlgOfb, AlgCbc, AlgCfb, AlgMac). В приведённом выше примере представлен режим гаммирования с обратной связью по выходу (OFB). В данном режиме для работы алгоритма требуется гамма, значение которой задаётся через переменную alg.gamma. Необходимо учесть, что данная переменная изменяется в процессе шифрования и для успешного расшифрования требуется установить начальное значение гаммы.

Базовый алгоритм

В этом разделе я покажу, как может выглядеть реализация на Rust. Более подробно вы можете посмотреть в исходниках на GitHub.

Базовый алгоритм шифрования выглядит намного проще чем его описание в стандарте:

pub fn encrypt_block(data: &mut Block128, keys: &[Block128; 10]) {    for i in 0..9 {        tfm_lsx(data, &keys[i]);    }    tfm_x(data, &keys[9]);}

Здесь производится девять итераций LSX-преобразования и затем ещё одну итерацию X-преобразования. Напомню длина блока 128 бит или 16 байт. Тип Block128 именно такого размера, элементы которого имеют тип u8.

Далее LSX-преобразование. Оно состоит из поочерёдного вызова X, S, и L преобразований.

fn tfm_lsx(data: &mut Block128, key: &Block128) {    tfm_x(data, key);    tfm_s(data);    tfm_l(data);}

В свою очередь X-преобразование делает сложение по модулю 2 (проще говоря - XOR) блока данных с ключом:

fn tfm_x(data: &mut Block128, key: &Block128) {    for i in 0..16 {        data[i] ^= key[i];    }}

В S-преобразовании в данные вносится нелинейность для чего к данным применяется подстановка (пи).

fn tfm_s(data: &mut Block128) {    for i in 0..16 {        data[i] = K_PI[data[i] as usize];    }}

L-преобразование производит 16 раз R-преобразование на данными.

fn tfm_l(data: &mut Block128) {    for _ in 0..16 {        tfm_r(data);    }}

И наконец R-преобразование:

fn tfm_r(data: &mut Block128) {    let temp = trf_linear(data);    data.rotate_right(1);    data[0] = temp;}

Данное преобразование лучше всего показать на рисунке. F линейное преобразование, которое соотносит 16 байтам однобайтовый результат, согласно выражению ниже.

Линейное преобразование:

Чтобы не вычислять значение линейного преобразования на каждой итерации L-преобразования, можно вычислить, своего рода, таблицу умножения. Таблица содержит 7 строк и 256 столбцов, что соответствует произведениям в поле Галуа коэффициентов на значения входных данных (произведения входных данных на коэффициент равный 1 нет смысла держать в таблице, т.к. результат равен входным данным).

Вычисление линейного преобразования по такой таблице умножения занимает O(1) времени и требует O(1) памяти, а точнее 7 * 256 = 1792 байта, что не так много для современного компьютера.

Собственно реализация:

fn trf_linear(data: &Block128) -> u8 {  // indexes:  0,  1,   2,   3,   4,   5,   6    // values:  16, 32, 133, 148, 192, 194, 251    let mut res = 0u8;    res ^= MULT_TABLE[3][data[0] as usize];    res ^= MULT_TABLE[1][data[1] as usize];    res ^= MULT_TABLE[2][data[2] as usize];    res ^= MULT_TABLE[0][data[3] as usize];    res ^= MULT_TABLE[5][data[4] as usize];    res ^= MULT_TABLE[4][data[5] as usize];    res ^= data[6];    res ^= MULT_TABLE[6][data[7] as usize];    res ^= data[8];    res ^= MULT_TABLE[4][data[9] as usize];    res ^= MULT_TABLE[5][data[10] as usize];    res ^= MULT_TABLE[0][data[11] as usize];    res ^= MULT_TABLE[2][data[12] as usize];    res ^= MULT_TABLE[1][data[13] as usize];    res ^= MULT_TABLE[3][data[14] as usize];    res ^= data[15];    res}

Что касается сложения в линейном преобразовании, то это всего лишь XOR. Таблицу умножения вы можете посмотреть в исходниках.

На этом всё, показывать обратные преобразования и режимы работы здесь будет уже лишним, и так уже кода получилось многовато, если что вы знаете где искать. Да, благодарю вас если вы дочитали до этого момента, всё же статья уже затянулась.

Про велосипед

Многие, наверно спросят, зачем писать то, что уже написано. Можно же использовать готовое от дяди Васи. В ответ я перечислю основные причины, которые подтолкнули меня на создание такого проекта.

  • Лицензирование ПО.

  • Качество ПО (безопасность, производительность, требования к памяти и тд.).

  • Интерфейс взаимодействия.

  • Зависимости.

  • Ну и конечно же опыт, который получаешь в процессе разработки.

Продолжение следует

Разместить все свои мысли в одной статье, на мой взгляд, было бы неправильно. Так можно быстро утомиться и потерять интерес к материалу. К тому же мне было бы приятно услышать ваше мнение и рассмотреть общие с вами темы.

На этом пока всё. Надеюсь, что данная статья поможет кому-нибудь в понимании алгоритма Кузнечик и языка Rust. Спасибо за внимание.

Отдельное спасибо Chris F за фотографии кузнечика.

Подробнее..

Актуальные методы расшифрования TLSSSL

10.02.2021 16:11:31 | Автор: admin

Привет, Хабр. В рамках курса Network engineer подготовили авторскую статью.

Также приглашаем всех желающих смотреть
открытый вебинар на тему NAT не Firewall. На нем участники вместе с экспертом рассмотрят NAT и его использование, разберут, почему NAT != firewall. Дополнительно рассмотрят различные виды конфигураций для разных ситуаций.


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

Проблематика и история

Разберемся немного с проблематикой и терминологией. На сегодняшний день наиболее популярными технологиями, которые применяются для шифрования данных, передаваемых по сети, являются SSL и TLS. Последняя сейчас является стандартом дефакто для взаимодействия по протоколу HTTPS. Кстати, именно в расшифровке этого протокола и будет заключаться практическая часть данной статьи. Для понимания процесса расшифровки данных мы должны знать такие понятия как:

  • Симметричная криптография

  • Ассиметричная криптография

  • Сертификат

  • Хранилище сертификатов

  • HSTS или Strict Transport Security технология, которая включена в современных браузерах для контроля над обязательным использованием HTTPS для взаимодействия с сервером.

Описание каждого из пунктов, указанных выше, выходит за тематику данной статьи поэтому если вы никогда не сталкивались с этими понятиями, то самое время их найти в Wiki и продолжить изучение статьи.

Практика

Практику будем проводить с использованием виртуальной лаборатории. Состав лаборатории:

  • Virtual Box;

  • Windows 8.1;

  • Ubuntu Server 20.04

Также для тестирования способов расшифровки трафика будем использовать устройство iPhonе SE.

Все машины должны быть подключены к сети "NAT Network". Именно этот тип подключения позволяет моделировать близкую к реальной топологию сети. В схеме есть хосты, которые могут взаимодействовать друг с другом, и есть хост, который используется как точка доступа в сеть Интернет. Приступим к практике.

Расшифровка трафика с использованием SQUID

Squid программное обеспечение, которая эмулирует функцию кэширующего сервера. Может быть использована для распределения нагрузки и логирования действий пользователей по протоколу HTTP в сети, кстати, и с HTTPS это ПО работать тоже умеет. Воспользуемся этой его функцией. К сожалению, использовать squid из репозитория не получится, необходимо собрать его самостоятельно:

```sh wget http://www.squid-cache.org/Versions/v4/squid-4.5.tar.gztar -xvzf squid-4.5.tar.gzcd squid-4.5./configure --with-openssl --enable-ssl-crtd --prefix=/usr/local/squidmakemake allmake install```

Самый действенный способ расшифровки взаимодействия между сервером и клиентом на сегодня это добавление доверенного корневого сертификата, поэтому перед началом расшифровки нужно его сгенерировать:

```sh cd /etc/squidmkdir ssl_certchown squid:squid -R ssl_certchmod 700 ssl_certcd ssl_certopenssl req -new -newkey rsa:2048 -sha256 -days 365 -nodes -x509 -extensions v3_ca -keyout myCA.pem  -out myCA.pemopenssl x509 -in myCA.pem -outform DER -out myCA.der```

Файл сертификата myCA.der можно использовать для браузера. Устанавливаем его в локальное хранилище и прописываем в качестве прокси сервер squid.

Настроим ссылку на вновь скомпилированный файл squid:

```shln -s /usr/local/squid/sbin/squid /usr/local/bin/squid```

Проинициализируем директорию для кэша:

```/usr/local/squid/libexec/security_file_certgen -c -s /var/lib/ssl_db -M 4MBchown squid:squid -R /var/lib/ssl_db```

Модифицируем конфиг:

```shnano /usr/local/squid/etc/squid.conf```

Должен получить следующий листинг:

```shacl SSL_ports port 443acl CONNECT method CONNECTacl manager proto cache_objecthttp_access deny !Safe_portshttp_access deny CONNECT !SSL_portshttp_access allow localhost managerhttp_access deny managerhttp_access allow localnethttp_access allow localhosthttp_access deny allhttp_port 3128cache_dir ufs /usr/local/squid/var/cache/squid 100 16 256coredump_dir /usr/local/squid/var/cache/squidrefresh_pattern ^ftp:144020%10080refresh_pattern ^gopher:14400%1440refresh_pattern -i (/cgi-bin/|\?) 00%0refresh_pattern -i \.(gif|png|jpg|jpeg|ico)$ 10080 90% 43200 override-expire ignore-no-cache ignore-no-store ignore-privaterefresh_pattern -i \.(iso|avi|wav|mp3|mp4|mpeg|swf|flv|x-flv)$ 43200 90% 432000 override-expire ignore-no-cache ignore-no-store ignore-privaterefresh_pattern -i \.(deb|rpm|exe|zip|tar|tgz|ram|rar|bin|ppt|doc|tiff)$ 10080 90% 43200 override-expire ignore-no-cache ignore-no-store ignore-privaterefresh_pattern -i \.index.(html|htm)$ 0 40% 10080refresh_pattern -i \.(html|htm|css|js)$ 1440 40% 40320refresh_pattern -i youtube.com/.* 10080 90% 43200refresh_pattern (/cgi-bin/|\?) 0 0% 0refresh_pattern .020%4320http_port 3128 ssl-bump \  cert=/etc/squid/ssl_cert/myCA.pem \  generate-host-certificates=on dynamic_cert_mem_cache_size=4MBsslcrtd_program /usr/local/squid/libexec/security_file_certgen -s /var/lib/ssl_db -M 4MBacl step1 at_step SslBump1ssl_bump peek allssl_bump stare allssl_bump bump allcache allow allaccess_log stdio:/usr/local/squid/var/logs/access.log combinedcache_store_log stdio:/usr/local/squid/var/logs/store.logcache_log stdio:/usr/local/squid/var/logs/cache.log```

Запускаем squid:

```shsquid -d 10 && tail -f /usr/local/squid/var/logs/access.log```

Результат проксирования:

Расшифровка взаимодействия с использованием CharlesProxy

В этом эксперименте будем использовать настоящую WiFi сеть с подключенным к нему устройством iPhone SE. Для расшифровки сетевого взаимодействия будем использовать специализированные программные продукты. Например charlesProxy. Продукт платный, но предоставляет бесплатный период использования. После запуска нужно выбрать опцию "Proxy > Start SSL Proxying":

После этого станет доступна ссылка на корневой сертификат для браузера или другого сетевого устройства. Установим сертификат на устройство:

После установки в в качестве доверенного в браузере или на устройстве, можно увидеть следующий результат:

Вывод

Расшифровка трафика это достаточно простой процесс, если есть правильный набор инструментов. Приведенные примеры можно использовать для анализа сетевых взаимодействий внутри сети. А так же можно применять эти методы для исследования тех данных, которые отправляются браузером или другим ПО в Интернет.


Узнать подробнее о курсе Network engineer.

Смотреть открытый вебинар на тему NAT не Firewall.

Подробнее..

Кодирование и Шифрование

22.03.2021 14:08:01 | Автор: admin

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

Но зачем это вообще нужно знать? Чтобы попросту не попасть в ситуацию, когда ваши личные данные, пароли от аккаунтов или банковских карт окажутся в руках мошенников. Как говорится: Доверяй, но проверяй

Важные аспекты в хранении данных, будь то на внешних серверах или домашнем компьютере, это прежде всего кодирования и шифрование. Но чем они отличаются друг от друга? Давайте разбираться!

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

Определения и различия

Кодирование процесс преобразования доступной нам информации в информацию понятную компьютерную.

Шифрование процесс изменения информации таким образом, чтобы её смогли получить только нужные пользователи.

Шифрование применялось и задолго до создания компьютеров и информатики как таковой. Но зачем? Цели её применения можно было понять из определения, но я опишу их ещё раз более подробно. Главные цели шифрования это:

  • конфиденциальность данные скрыты от посторонних

  • целостность предотвращение изменения информации

  • идентифицируемость возможность определить отправителя данных и невозможность их отправки без отправителя

Оценить стойкость шифра можно с помощью криптографической стойкости.

Криптографическая стойкость это свойство шифра противостоять криптоанализу, изучению и дешифровки шифра.

Криптостойкость шифра делится на две основные системы: абсолютно стойкие системы и достаточно стойкие системы.

Абсолютно стойкие системы системы не подверженные криптоанализу. Основные критерии абсолютно стойких систем:

  • Ключи должны генерироваться для каждого сообщения отдельно

  • Генерация ключей независима

  • Длинна ключа должна быть не меньше длинны сообщения

К сожалению, такие системы не удобны в своём использовании: появляется передача излишней информации, которая требует мощных и сложных устройств. Поэтому на деле применяются достаточно стойкие системы.

Достаточно стойкие системы системы не могут обеспечить полную защиту данных, но гораздо удобнее абсолютно стойких. Надежность таких систем зависит от возможностей крипто аналитика:

  • Количества перехваченных сообщений

  • Времени и вычислительных способностей

А также от вычислительной сложности шифра.

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

История шифрования

Шифрование берет своё начало ещё из древних времен. Примерно 1300 лет до нашей эры был создан один из первых методов шифрования Атбаш. Принцип шифрования заключается в простой подставке символов по формуле:n-i+1, где:

  • n количество символов в алфавите

  • i порядковый номер символа.

Шифр получил своё название в честь первой, последней, второй и предпоследней буквы Еврейского алфавита - алеф, тав, бет, шин. Такой шифр имеет низку криптографическую стойкость, потому как алгоритм шифрования довольно прост

С тех самых пор шифрование активно развивалось вместе с развитием нашей цивилизации

Хоть шифры и менялись, но их принцип нет для расшифровки сообщения требуется ключ. В случае с Абашем ключом может считать последовательность порядковых номеров исходных символов, но этот ключ ещё надо как-то передать. Методы шифрования, которые используются сейчас, научились-таки передавать ключи по открытым и незащищённым каналам связи. Казалось бы, передача ключей шифрования по обычным каналам это добровольное жертвование своими данными, но не всё так просто. Разберём это на примере популярного алгоритма шифрования RSA, разработанного в 1977 году.

Первым делом выбирается два случайный простых числа, которые перемножаются друг на друга именно это и есть открытый ключ.

К слову: Простые числа это те числа, которые могут делиться без остатка либо на 1, либо на себя.

Длинна таких чисел может быть абсолютно любая. К примеру, возьмем два простых числа 223 и 13. Их произведение 2899 будет являться открытым ключом, который мы и будем передавать по открытому каналу связи. Далее нам необходимо вычислить функцию Эйлера для произведения этих чисел.

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

Возможно, звучит непонятно, но давайте это разберем на небольшом примере:

(26) [фи от двадцати шести] = какому-то числу чисел, которое всегда будет меньше 26, а сами числа должны иметь только один общий делитель единицу с 26.

Давайте считать:

1 подходит всегда, идем дальше;

2 делится и на 2, и на 1, как и число 26, - не подходит;

3 делится и на 3, и на 1, а вот число 26 не делится на 3, - подходит;

4 имеет общие делители 2 и 1 с 26 - не подходит;

5 только на 1 - подходит;

6 на 2 и 1 - не подходит;

7 только на 1 подходит;

и так далее до 25.

Общее количество таких чисел будет равно 12. А найти это число можно по формуле: (n*k) = (n-1)(k-1) в нашем случае 26 можно представить как 2 * 13, тогда получим (26) = (2 * 130) = (2-1)*(13-1) = 1 * 12 = 12

Теперь, когда мы знаем, что такое функция Эйлера и умеем её вычислять найдем её для нашего открытого ключа (2899) = (223 * 13) =(223 1)*(13-1) = 222 * 12 = 2664

После чего нам нужно найти открытую экспоненту. Не пугайтесь, тут будет гораздо проще чем с функцией Эйлера.

Открытая экспонента это любое простое число, которое не делится на функцию Эйлера. Для примера возьмем 13. 13 не делится нацело на число 2664. Вообще открытую экспоненту лучше выбирать по возрастанию простым перебором, а не просто брать случайную. Так для нашего примера разумнее было бы взять число 5, но давайте рассмотрим на примере 13

Следующий шаг закрытая экспонента. Вычисляется она банальным перебором по этому равенству: d * e mod (n) = 1, где

  • (n) - функция Эйлера

  • e открытая экспонента

  • mod остаток отделения

а число d, которое и является закрытой экспонентой, мы должны подобрать перебором, либо попытаться выразить через формулу d = ceil((n) / e), где ceil округление в большую сторону.

В обоих случаях у нас получится число 205

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

Теперь этому человеку нужно отправить нам сообщение, для простоты предположим, что это какое-то число, например: 92. Для этого ему нужно отправить нам остаток от деления 92 в степени открытой экспоненты на открытый ключ T ^ e mod n, где

  • T шифруемый текст

  • e открытая экспонента

  • n открытый ключ

  • mod остаток от деления

92 ^ 13 mod 2899 = 235. Именно число 235 он нам и отправит.

Предположим, что и в этот раз сообщение перехватили, но нам оно всё так же дошло

Для расшифровки сообщения нам необходимо зашифрованное сообщение возвести в степень закрытой экспонентой и вычислить остаток от деления на открытый ключ C ^ d mod n, где

  • С зашифрованный текст

  • d закрытая экспонента

  • n открытый ключ

  • mod остаток от деления

235 ^ 205 mod 2899 = 92.

Вуаля, и мы имеет исходное число. Но, что насчет перехваченных сообщений? У злоумышленника есть сообщение, ключ и экспонента, но как мы помни для дешифровки ему ещё нужна секретная экспонента, она же секретный ключ, но для того, чтобы вычислить её, ему придется разложить исходный ключ 2899 на множители, а сделать это не так уж и просто, особенно когда вместо двух чисел 223 и 13, будут использовать числа длинной несколько десятков символов

Но ничто в мире не идеально, в том числе и этот метод.

Его первый недостаток это подборка пары чисел для открытого ключа. Нам нужно не просто сгенерировать случайно число, но ещё и проверить на то простое ли оно. На сегодняшний нет методов, которые позволяют делать это сверх быстро.

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

Третий недостаток подбор и перебор чисел для экспонент.

Четвертый длинна ключей. Чем больше длинна, тем медленнее идет процесс декодирования, поэтому разработчики пытаются использовать наименьшие по длиннее ключи и экспоненты. Даже я акцентировал на это внимание, когда говорил, что лучше взять число 5, вместо 13 для открытой экспоненты. Именно из-за этого и происходит большая часть взломов и утечек данных

Но не стоит печалиться, ведь как я и говорил: криптография и шифрование развивается вместе с развитием цивилизации. Поэтому довольно скоро все мы будем шифровать свои данные с помощью Квантового шифрование.

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

Есть оптоволокно, по которому передаются единичные фотоны света. Мы, как отправитель может сгенерировать абсолютно любой двоичный ключ, по тому же принципу квантовой супер позиции, ну или использовать обычные генераторы псевдослучайных чисел. Допустим мы хотим передать ключ 101001011. Для этого нам нужно принять за обозначение какое положение фотона соответствует единице, а какое нулю. Представим, что вертикальное положение это 1, а горизонтальное 0. Если оставить все так, то от передачи ключей таким образом не будет никакого смысла, ведь тогда злоумышленник всегда сможет измерить фотон, получить его значение, создать и отправить точно такой же обратно человеку, которому мы хоти передать ключ. Поэтому были введены ещё два положение диагональные. Предоставим вертикальную волну, или же значение 1 и отклоним её на 45 градусов влево. Это будет вторая единица. Вернемся обратно и отклоним на 45 градусов вправо это будет второй 0.

Вернемся к нашему ключу 101001011. Мы случайным образом выбираем направление обычное или диагональное. Для удобства присвоим обычному номер 1, а диагональному 2.

Давайте отправим ключ 1(1), 0(2), 1(1), 0(1), 0(1), 1(2), 0(2), 1(1), 1(2). Теперь человеку, которому мы отправляем ключ, нужно точно так же, совершенно случайно, выбрать случайное направление.

Допустим он выбрал направления: 221111212. Поскольку есть всего 2 плоскости отправки: 1 и 2, они же называются: канонический и диагональный базис, то шанс того, что он выбрал правильные направления 50%.

Если он угадал базис он получил верное значение, если нет неверное. Учитывая его направления, он получил: 001000011. Теперь нужно отсеять неправильные значения: можно сделать это обменом базисов по любому, даже не защищенному, каналу связи. После этого у нас обоих останется ключ: 0100011. Теперь с помощью его мы можем передавать и кодировать сообщения по обычному методу шифрования.

А что, если кто-то перехватит отправку кода? Тогда ему придется точно также подбирать случайным образом базисы, что добавит ещё 25% погрешности при получении кода человеку, которому мы изначально и отправили его. Чтобы проверить это, после отсеивания мы, как отправитель, должны проверить сколько процентов кода оказалось не верным. В нашем 1 случае это (9 7)/9 * 100% = 22%, если это число будет больше 50%, то мы начнем повторную отправку ключей, до тех пор, пока погрешность не будет меньше 50%

Заключение

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

Список литературы и материалов:

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru