Как мы делали приложение «Фотон-2024»
Tags: mobile, flutter, devops, digitalresistance
Published: 2024-04-09 12:58:49

В рамках кампании против Путина на президентских выборах в России в 2024 году IT-отдел команды Навального разработал приложение под Android и iOS, которое помогало  случайным образом выбирать любого другого кандидата. Для этого приложение использовало аппаратный генератор случайных чисел, основанный на квантовом эффекте. Эту идею предложил Алексей Навальный. 

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


![Основные экраны приложения](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5s2rt76g17dwypz0e30r.png)

## Выбор устройства и покупка квантового генератора

Алексей темой случайности озаботился еще во время изучения языка Python. Когда он столкнулся с функцией random, то заинтересовался, как работает случайность в скриптах, которые он писал во время обучения. Откуда конкретно берется случайное число? 

В 2019 году он узнал о том, что компьютерная случайность не есть истинная случайность, что это псевдослучайность. Когда выполняется random.random() в скрипте, случайное число не рождается во Вселенной из ничего, а основано на простой математике — на так называемых [вихрях Мерсенна](https://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%85%D1%80%D1%8C_%D0%9C%D0%B5%D1%80%D1%81%D0%B5%D0%BD%D0%BD%D0%B0). 

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

В начале XX века Эйнштейн и Бор начали знаменитый спор о том, являются ли случайные процессы в квантовом мире лишь кажущимися, случайными, или же они истинно случайные. Этот спор с Бором многие знают по фразе Эйнштейна «Бог не играет в кости!». Так Эйнштейн выразил позицию, что истинной случайности нет, а есть просто скрытые параметры, которые и создают кажущуюся случайность в квантовых процессах. Через полвека после спора наука смогла проверить экспериментально через [неравенства Белла](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%D1%81%D1%82%D0%B2%D0%B0_%D0%91%D0%B5%D0%BB%D0%BB%D0%B0), кто же прав, — и прав оказался Бор. Его интерпретация квантовой механики, называемая копенгагенской, до сих пор [главенствует](https://arxiv.org/pdf/1301.1069.pdf) в науке.

Когда в конце сентября 2023 возник [спор о стратегии голосования](https://meduza.io/feature/2023/10/02/kats-prizval-hodorkovskogo-i-komandu-navalnogo-ob-edinitsya-protiv-putina-pered-vyborami-2024-goda-i-tut-nachalos) на президентских выборах, Алексей вспомнил идею о случайности, создаваемой фотонами, и предложил переложить моральную ответственность выбора «любого другого кандидата», за которого можно было бы проголосовать в рамках кампании против Путина, на генератор истинно случайных чисел. Так родилась идея приложения «Фотон», где реальный фотон будет определять случайного кандидата.

![Письмо Алексея с идеей приложения](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0y3o3hwqy5hj9ewt8vfg.png)

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

От идеи получать истинную случайность по [API в AWS](https://aws.amazon.com/marketplace/pp/prodview-246kyrfjo3bag), где случайность брали бы у австралийских физиков, сделавших лазерную установку, и получали бы энтропию из квантовых флуктуаций вакуума, он отказался.

![Письмо Навального о персональном фотоне](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74pdwrqhqwfsy3fflu2a.png)

Для надежности мы решили использовать две установки, основную и резервную. Обе от ID Quantique, обе на квантовых эффектах, но на разных. Одна использует [дробовой шум](https://ru.wikipedia.org/wiki/%D0%94%D1%80%D0%BE%D0%B1%D0%BE%D0%B2%D0%BE%D0%B9_%D1%88%D1%83%D0%BC) при облучении слабым источником света CMOS-матрицы, что вызывает флуктуации количества детектируемых квантов света каждой из ячеек матрицы. Эта установка, [Quantis QRNG PCIe](https://marketing.idquantique.com/acton/attachment/11868/f-9a58efcf-5169-45ba-8a1c-ceea3761581f/1/-/-/-/-/Quantis%20QRNG%20PCIe%20New%20Generation_Brochure.pdf), дает порядка 40 Мбит/с энтропии. Вторая, [Quantis QRNG USB](https://marketing.idquantique.com/acton/attachment/11868/f-021f/1/-/-/-/-/Quantis%20QRNG_Brochure.pdf), подключаемая по USB и содержащая полупрозрачное зеркало, на которое направляется поток одиночных фотонов, выдает около 4 Мбит/с энтропии. 

![Изобаржение квантового генератора](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pw39gjb4fneb2nsyq4ae.png)

Квантовый генератор случайных чисел с полупрозрачным зеркалом работает следующим образом. Источник раз за разом испускает одиночный квант света. Тот налетает на полупрозрачное зеркало, через которое проходит 50% света, а 50% отражается. И, поскольку фотон неделим (слава Планку и Эйнштейну), он либо пролетает через зеркало, либо отражается. Детекторы определяют, куда попал фотон, и так генерируется случайный выбор, 0 или 1. Для компенсации неравнозначности нуля и единицы в случае, если зеркало имеет не идеально точную 50-процентную пропускную способность, используется постобработка по [алгоритму Джона фон Неймана](https://babel.hathitrust.org/cgi/pt?id=osu.32435030295547&view=image&seq=48). С ее помощью определенные выше биты группируются по два, что в четыре раза уменьшает получаемую энтропию, но делает ее unbiased.

![Схема работы квантового генератора случайных чисел](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kfnyypsppzxb6fx4y0q0.png)
_Схема работы квантового генератора случайных чисел_

## Физическое подключение и первоначальная настройка 

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

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

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

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

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

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

![Сертификат квантового генератора](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bju8o3xqx91bmk3l17l9.png)

## Архитектура приложения и алгоритм работы

В связи с тем, что пользователю приложения необходимо было подключаться к квантовому генератору, размещенному в офисе ФБК, без сетевого соединения на стороне приложения было никак не обойтись. А значит, Роскомнадзор имел бы все шансы идентифицировать пользователя и попытаться заблокировать адреса, по которым он обращался к генератору при помощи СОРМ и ТСПУ. Мы решили построить архитектуру приложения, исходя из устойчивости к потенциальным блокировкам, нагрузкам от большого количества запросов и сохранения приватности пользователя. 

Приложение должно было быть реализовано на двух платформах (Android и iOS), а также работать на планшетах — выбор пал на Flutter. В будущем плюсами данного выбора станут скорость и гибкость в разработке, но из-за отсутствия достаточного количества библиотек некоторые решения пришлось писать вручную. В нашем случае это была кастомная библиотека DNS over HTTPS (DoH) для работы по протоколу [RFC 8484](https://datatracker.ietf.org/doc/html/rfc8484). Вместо стандартного сетевого соединения клиент генерировал случайную строку из 20 символов a-z0-9 и через DoH запрашивал один поддомен дискавери-домена (скажем, `w4a1s27c81syputinvor.photon.navalny-team.org`) через случайный публичный резолвер, например 1.1.1.1 от Cloudflare или 8.8.8.8 от Google. В качестве фолбэка было добавлено еще несколько альтернативных, чтобы Роскомнадзор не решался блокировать любой из них под угрозой сломать множество приложений российского сегмента интернета.

Приложение проверяло, чтобы ответ сервера состоял ровно из двух записей TXT: с префиксами «N» для имени кандидата и «D» — для его описания в utf-8. Необходимо было, чтобы записи были минимально возможного размера, чтобы ответ влез в ограничения ответа DNS — 512 байт. Разгоняться с контентом было нельзя, и поэтому приложение не выдавало длинных биографий кандидатов или их фотографий, тем более что особого значения это не имело. Если вы читаете эту статью вскоре после ее публикации, возможно, сервер еще работает и вы можете сгенерировать себе еще кандидатов командой `host -t txt w4a1s27c81syputinvor.photon.navalny-team.org`.

![Схема архитектуры приложения ФОТОН-2024](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zlrcq441nel2ngrjeic.png)
_Схема архитектуры приложения «Фотон-2024»_

Клиентская библиотека генератора случайных чисел написана на C и представляет собой довольно простой интерфейс: перечисление подключенных устройств, получение версий, открытие-закрытие сессии и, собственно, запрос на получение случайных чисел.

Сервер HW RNG, к которому был подключен генератор, при запуске устанавливал TCP-соединение с DNS-сервером (по отдельному внутреннему порту) и без остановки лил туда случайные числа. Поскольку сервер считывал данные из сокета только по мере поступления запросов от пользователей, рано или поздно backpressure от сервера заставлял клиент заблокироваться. Энтропию из буфера брали в порядке [LIFO](https://ru.wikipedia.org/wiki/LIFO), чтобы для пользователей брались «свежие» фотоны.

DNS-сервер реализовывал основную логику бэкенда. При запуске он считывал JSON-файл с кандидатами с бакета GCS и обновлял его в фоне каждые 10 секунд на случай изменений. Сервер считывал данные от HW RNG и писал их в буфер фиксированного размера на случай поступления спайков клиентских запросов. Размер буфера можно сделать таким, сколько памяти не жалко — хоть гигабайт.

Сервер получал запрос с утвержденным адресом нашего дискавери, брал нужное количество байт энтропии и делал выбор кандидата в соответствии с текущим конфигом, полученным с GCS. Ответ упаковывался в TXT-записи DNS.

Чтобы сделать работу системы максимально надежной, было реализовано несколько аварийных режимов:

- Если по каким-то причинам энтропии в буфере было бы недостаточно (например, сломался сервер HW RNG или пропал интернет в офисе ФБК), DNS-сервер сгенерировал бы случайное число из /dev/urandom. Пользователь бы об этом не узнал, но администраторы получили бы оповещение. К слову сказать, этого так ни разу в продакшене и не случилось — все случайные числа были сгенерированы настоящими фотонами.
- Если бы у пользователя оборвалось соединение, отключился интернет или приложение было бы заблокировано Роскомнадзором, на такой случай в него была заложена функция локального рандома для выбора кандидата из списка, которое приложение в фоновом режиме получало с GCS.
- На случай, если бы свежеустановленное приложение так ни разу и не смогло обновить список кандидатов и DNS-сервер не отвечал, в нем еще был захардкожен список с кандидатами на дату сборки приложения.

Если приложению не удавалось подключиться к квантовому генератору в офисе ФБК и оно было вынуждено делать локальную генерацию, то оно честно в этом признавалось: 


![Экран сбой подключения к Фотону](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjclhmbk7bd398wox3c2.png)

Для хранения данных в приложении использовались защищенные хранилища — Keychain (iOS) и KeyStore (Android).

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

Бот «Умного голосования» для мессенджеров аналогично обращался к DNS серверу, получал «квантового кандидата», и ответ в нем был мгновенным.

## Заключение
 
Для контроля работы и сбора статистики были организованы дашборды в Grafana. Записывались запросы к квантовому генератору и состояние буфера энтропии. Был подключен Firebase c инструментарием Google Analytics на стороне приложения.

«Фотонная пушка» сработала более 650 тысяч раз.


![График распределения запросов к квантовому генератору из приложения и телеграм-бота
](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/07lhj3064cnwrptq6wzj.png)
_График распределения запросов к квантовому генератору из приложения и телеграм-бота_

Приложением воспользовались более 70 тысяч человек. Оно попало в топы рейтингов сторов в своей категории. Роскомнадзор даже [ограничил доступ к сайту Google Play Market](https://t.me/news_sirena/25646) в день выпуска.
 
Задача сделать приложение, которое подключалось бы к квантовому генератору и выдавало бы один результат, звучит просто. Но в условиях ограничений интернета в России она вынуждает разработчиков продумывать в своей архитектуре дополнительные меры безопасности и дополнительный функционал для борьбы с репрессивными органами, которые не только ограничивают свободу, но и ломают российский интернет. Нашим долгом было воплотить идею Алексея Навального и продолжить борьбу с Путиным.

**Все это стало возможным только благодаря вашей помощи. Поддержать нашу работу можно по ссылке: [donate.acf.international/ru](http://donate.acf.international/ru). Чтобы сделать пожертвования еще безопаснее, мы даже добавили на сайт [функцию генерации одноразового биткоин-адреса](https://donate.acf.international/ru#crypto) и обязательное смешивание всех полученных средств через миксер.**