惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
S
Securelist
J
Java Code Geeks
Recorded Future
Recorded Future
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
S
Secure Thoughts
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Docker
Martin Fowler
Martin Fowler
The Last Watchdog
The Last Watchdog
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
O
OpenAI News
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
H
Heimdal Security Blog
SecWiki News
SecWiki News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 【当耐特】
T
Troy Hunt's Blog
L
LINUX DO - 最新话题
Hacker News: Ask HN
Hacker News: Ask HN
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
Netflix TechBlog - Medium
A
Arctic Wolf
The Hacker News
The Hacker News
I
Intezer
S
Schneier on Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Apple Machine Learning Research
Apple Machine Learning Research
L
Lohrmann on Cybersecurity
宝玉的分享
宝玉的分享
P
Privacy & Cybersecurity Law Blog
Stack Overflow Blog
Stack Overflow Blog
T
Tor Project blog
小众软件
小众软件
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
Jina AI
Jina AI

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Рюкзачная криптосистема Шора-Ривеста
Илья · 2026-06-25 · via Все публикации подряд на Хабре

Рюкзачная криптосистема Шора-Ривеста

Сложный

13 мин

11

В этой статье мы рассмотрим рюкзачную, или ранцевую, криптосистему Шора-Ривеста (Chor-Rivest knapsack). Это один из немногих алгоритмов классической криптографии, который можно рассматривать как постквантовый кандидат. Несмотря на то что сегодня он не считается современной и надёжной постквантовой криптосистемой, его всё равно интересно разобрать как один из нестандартных вариантов такого рода.

Математическая база

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

Структура статьи

Введение в тeорию сложности вычислений

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

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

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

Источник

Vadhan, Salil P. 2011. Computational complexity. In Encyclopedia of Cryptography and Security, second edition, ed. Henk C.A. van Tilborg and Sushil Jajodia. New York: Springer.

Пример

O(n^3 + 3n + 1) = O(n^3)

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

Пример

Если T = O(n), то удвоение входных данных приведет к удвоению времени работы алгоритма, а если T = O(2^n), то добавление одного бита к входным данным приведет к удвоению времени работы алгоритма.

В зависимости от функции сложности существуют классы сложности задач. Пусть n - длина битовый строки - входных параметров алгоритма. Алгоритмы, временная сложность которых может быть записана как O(n^c), где c - константа, называются алгоритмами с полиномиальным временем, так как их функция сложности полиномиальна. Такие задачи считаются "легкими", они могут быть эффективно решены. Их относят к классу сложности P - класс задач, которые могут быть решены за полиномиальное время. Однако если временная сложность алгоритма задается, например, как O(c^n), где c - константа, то говорят об экспоненциальной сложности и экспоненциальном времени. Такие задачи быстро становятся нерешаемыми при росте размера входных данных и к классу P уже не относятся.

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

Задача об укладке рюкзака

Одной из "трудных" задач является задача об укладке рюкзака. В ее основе лежит следующая идея. Есть рюкзак, вместимость которого ограничена, и множество предметов, которые в него можно положить. Необходимо найти подмножество предметов, которое этот рюкзак заполнит доверху. Эта задача интересна тем, что зная подмножество предметов, уложенных в рюкзак, легко подсчитать его вес, однако, зная вес рюкзака, трудно определить подмножество предметов. Другой вариант задачи, в котором в рюкзак можно положить не более одного экземпляра предмета каждого типа, называется рюкзак 0-1.

Более формально, задача о рюкзаке 0-1 является NP-полной задачей комбинаторной оптимизации. Математическая задача формулируется следующим образом: имеется множество A = \{ a_i | 0 \leq i \leq n-1 \} неотрицательных целых чисел и неотрицательное целое число S. Необходимо найти решение в целых числах для

\sum_{i=0}^{n-1} a_i x_i = S , где x_i \in \{ 0, 1 \} для всех i.

Другой вариант постановки задачи о рюкзаке - снять ограничение 0-1, требуя только чтобы все x_i были неотрицательными целыми числами, и ограничить их вес:

\sum_{i=0}^{n-1} x_i \leq h.

Источник

Chor B., Rivest R. L. A Knapsack-Type Public Key Cryptosystem Based on Arithmetic in Finite Fields. // IEEE Transactions on information theory. 1988. Vol. 34, № 5. pp. 901–909.

Рюкзачным вектором A = (a_0, \dots, a_{n-1}) называется упорядоченный набор из n предметов, где a_i - вес i-го предмета, 0 \leq i \leq n-1.

Источник

Саломаа А. Криптография с открытым ключом: Пеp. с англ. — М.: Миp, 1995. с. 73-161.

Пример

Пусть A = (3, 5, 10, 7, 8) - рюкзачный вектор длины 5. Тогда число 25 можно представить в виде суммы элементов рюкзачного вектора 25 = 3 + 5 + 10 + 7. Разложение числа по элементам рюкзачного вектора можно записать в виде бинарного вектора. Для предыдущего примера этот вектор будет равен (1, 1, 1, 1, 0), где 1 стоит на месте тех элементов, которые участвовали в разложении. При этом, просуммировав элементы рюкзачного вектора, на месте которых стоят 1, можно получить исходное число. Этот факт можно использовать для построения криптосистемы, однако стоит заметить, что в приведенном нами примере одно и то же число может быть представлено несколькими способами, из-за чего преобразования становятся неоднозначными: 8 можно представить как (1, 1, 0, 0, 0) или как (0, 0, 0, 0, 1).

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

Вектор A = ( a_0, \dots, a_{n-1} ) называется сврехрастущим, если \sum_{i=0}^{j-1} a_i < a_j для j = 1, \dots, n-1,

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

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

Алгоритм.

Вход: A, S; Выход: \vec{x};

  1. i \leftarrow n-1

  2. пока i >= 0

    1. если a_i \leq S, то

      1. x_i \leftarrow 1

      2. S \leftarrow S - a_i

    2. иначе x_i \leftarrow 0

    3. i \leftarrow i-1

  3. вернуть \vec{x}

Также, не являются надежными рюкзаки с низкой плотностью, они подвержены ряду атак ("low-density" attack).

Источник

J. C. Lagarias and A. M. Odlyzko. Solving Low-Density Subset Sum Problems. Journal of the Association for Computing Machinery. January 1985. Vol. 32, № 1, pp. 229-246.

Плотность рюкзака определяется как отношение количества элементов в нем к размеру в битах его элементов (или размеру максимального элемента):

d(A) = \frac{n}{ log_2(max A) }

Пример

Для сверхрастущего вектора A = (1, 2, 4, 8, 16, 32) d(A) = 6/5.

Рюкзачная криптосистема Шора-Ривеста

Существуют различные варианты криптосистем на основе задачи об укладке рюкзака. Впервые система шифрования на основе этой задачи была предложена в 1978 году Ральфом Мерклом и Мартином Хеллманом. Однако некоторые варианты этой криптосистемы были взломаны. Позднее была предложена рюкзачная криптосистема Шора-Ривеста, которая будет рассмотрена далее.

В общем случае можно выделить основные принципы построения криптосистемы с открытым ключом на основе задачи об укладке рюкзака:

  1. Составляется трудная задача T, нерешаемая за полиномиальное время.

  2. Из T выделяется легкая задача T_{easy}, которую можно решить за полиномиальное время или даже быстрее.

  3. С помощью некоторых преобразований легкая задача T_{easy} превращается в трудную T_{hard}, не имеющую никакого сходства с T_{easy}, при этом процедура получения T_{easy} из T_{hard} держится в секрете.

  4. T_{hard} используется для конструирования открытой функции зашифрования, а T_{easy} - для конструирования закрытой функции расшифрования. Таким образом для злоумышленника процедура дешифрования будет заключаться в решении T_{hard}, а для добросовестного пользователя, знающего секрет, расшифрование будет осуществляться посредством решения T_{easy}.

Источник

Иванов М.А., Михайлов Д.М., Чугунков И.В., Ковалев А.В., Мацук Н.А. Стохастические методы и средства защиты информации в компьютерных системах и сетях. / Под ред. И.Ю. Жукова. – М.: КУДИЦ-ПРЕСС, 2009. c. 131-134.

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

Источники
  1. Chor B., Rivest R. L. A Knapsack-Type Public Key Cryptosystem Based on Arithmetic in Finite Fields. // IEEE Transactions on information theory. 1988. Vol. 34, № 5. pp. 901–909.

  2. Alfred J. Menezes, Paul C. van Oorschot, Scott A. Vanstone. Handbook of Applied Cryptography, 1996, pp. 303-305.

Для описания работы криптосистемы нам понадобится следующая теорема.

Теорема (Bose-Chowla). Для любого простого p и натурального h \geq 2 существует последовательность целых чисел A = ( a_0, \dots, a_{p-1} ), удовлетворяющая следующим условиям:

(1)

1 \leq  a_i \leq p^h - 1, \: i = 0, \dots, p-1

(2) если (x_0, x_1, \dots, x_{p-1}) и (y_0, y_1, \dots, y_{p-1}) - два вектора с неотрицательными целыми координатами, удовлетворяющие ограничениям

(x_0, x_1, \dots, x_{p-1}) \neq (y_0, y_1, \dots, y_{p-1})\sum_{i=0}^{p-1} x_i \leq h - 1, \sum_{i=0}^{p-1} y_i \leq h - 1 \quad \text{или} \quad \sum_{i=0}^{p-1} x_i = \sum_{i=0}^{p-1} y_i = h

то \sum_{i=0}^{p-1} x_i a_i \neq \sum_{i=0}^{p-1} y_i a_i.

Доказательство

Рассмотрим расширение GF(p) \subset GF(p^h). Пусть t \in GF(p^h) - алгебраическое число степени h над полем GF(p) (т.е. минимальный многочлен t из кольца многочленов GF(p)[x] имеет степень h), g - примитивный элемент поля (и, соответственно, генератор мультипликативной группы) GF(p^h). Рассмотрим аддитивный сдвиг основного поля GF(p) на элемент t:

t + GF(p) = \{ t + \alpha_i | \alpha_i \in GF(p) \} \subset GF(p^h)

Пусть a_i = log_g (t + \alpha_i), \alpha_i \in GF(p)- дискретный логарифм элемента t + \alpha_iпо основанию g в поле GF(p^h). Тогда все a_iлежат в интервале [1, p^h - 1] (т.к. GF(p^h)^* = \{ g^n | 1 \leq n \leq p^h - 1 \}, p^h - 1 \equiv 0 \: (\textrm{mod} \: p^h - 1)), т.е. удовлетворяют первому условию теоремы. Проверим, что все a_i также удовлетворяют и второму условию. Рассмотрим два вектора \vec{x}, \vec{y} с неотрицательными целыми координатами, удовлетворяющие ограничениям (1) и (2), и пусть

\sum_{i=0}^{p-1} x_i a_i = \sum_{i=0}^{p-1} y_i a_i

Тогда выполняется следующее равенство:

g^{ \sum_{i=0}^{p-1} x_i a_i } =  g^{ \sum_{i=0}^{p-1} y_i a_i }

и следовательно

\prod_{i=0}^{p-1} (g^{a_i})^{x_i} = \prod_{i=0}^{p-1} (g^{a_i})^{y_i}

Подставляя g^{a_i} = t + \alpha_i, получим

(t + \alpha_1)^{x_1} (t + \alpha_2)^{x_2} \dots (t + \alpha_{p-1})^{x_{p-1}} = (t + \alpha_1)^{y_1} (t + \alpha_2)^{y_2} \dots (t + \alpha_{p-1})^{y_{p-1}}

Обе части последнего равенства - различные (по условию (1)) приведенные многочлены, степени которых либо совпадают и равны h, либо меньше h (по условию (2)), поэтому, вычитая их, мы получим ненулевой многочлен степени меньше h с коэффициентами в GF(p), корнем которого является t. Это противоречит тому факту, что t является алгебраическим числом степени h над полем GF(p).

Замечание 1.

Из доказательства видно, что теорема выполняется не только в целых числах, но и по модулю p^h - 1 (т.к. оно основано на свойствах мультипликативной группы GF(p^h)^*, |GF(p^h)^*| = p^h - 1).

Замечание 2.

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

Генерация параметров криптосистемы:

  1. Выбрать конечное поле GF(p^h), где p - степень простого числа, h \leq p, в котором можно эффективно решать задачу дискретного логарифмирования.

  2. Выбрать случайный элемент t \in GF(p^h), являющийся алгебраическим числом степени h над GF(p). Для этого необходимо выбрать случайный неприводимый нормированный (старший коэффициент равен единице) многочлен степени h в кольце многочленов GF(p)[t] и представить поле GF(p^h) как GF(p)[t] / \langle f(t) \rangle (т.е. элементы поля GF(p^h) - многочлены степени не больше h с коэффициентами в GF(p), арифметические операции над которыми осуществляются по модулю f(t)).

  3. Выбрать случайный примитивный элемент поля g \in GF(p^h) (т.е. g - генератор мультипликативной группы GF(p^h)^*).

  4. В соответствии с Теоремой Bose-Chowla вычислить дискретные логарифмы a_i = log_g (t + \alpha_i) для всех \alpha_i \in GF(p).

  5. Выбрать случайную перестановку \pi: \{ 0, 1, \dots, p-1 \} \rightarrow \{ 0, 1, \dots, p-1 \} и перемешать a_i: b_i = a_{\pi(i)}.

  6. Выбрать случайное число d, 0 \leq d \leq p^h - 2 и добавить шум: c_i = b_i + d \: (\textrm{mod} \: p^h - 1), 0 \leq i \leq p - 1.

Открытым (публичным) ключом является ((c_0, c_1, \dots, c_{p-1}), p, h), закрытым (секретным) ключом - (f(t), g, \pi^{-1}, d).

Шифрование:

Чтобы зашифровать бинарное сообщение M = (x_0, x_1, \dots, x_{p-1}) длины p и веса (количество 1) в точности h, необходимо сложить c_i, которые соответствуют битам, равным 1:

E(M) = \sum_{i=0}^{p-1} x_i c_i \: (\textrm{mod} \: p^h - 1)

Расшифрование:

  1. Пусть c = E(M) - зашифрованное сообщение, вычислить c' = c - hd \: (\textrm{mod} \: p^h-1).

  2. Вычислить u(t) = g^{c'} \: (\textrm{mod} \: f(t)).

  3. Вычислить s(t) = u(t) + f(t) - нормированный многочлен степени h в GF(p)[t].

  4. Разложить s(t) на линейные множители над GF(p): s(t) = (t + \alpha_1) (t + \alpha_2) \dots (t + \alpha_h). Все h корней этого многочлена могут быть найдены последовательной подстановкой элементов GF(p). Таким образом мы найдем h корней \alpha_i \in GF(p), 1 \leq i \leq h.

  5. Применить обратную перестановку \pi^{-1} к полученным корням. Получим h индексов \pi^{-1}(\alpha_i), 1 \leq i \leq h, на месте которых в сообщении M = (x_0, \dots, x_{p-1}) будут стоять 1, остальные x_i будут равняться 0.

Доказательство

Преобразование произвольных битовых строк

При описании криптосистемы мы предполагали, что сообщение M имеет длину p и вес h. Однако произвольные битовые последовательности не удовлетворяют этому свойству. Далее рассмотрим метод, с помощью которого можно привести произвольную битовую строку к требуемому виду.

Источник

T. M. Cover. Enumerative source encoding. // IEEE Transactions on information theory. 1973. Vol. IT-19, pp. 73-77.

Пусть \{ 0, 1 \}^n - множество бинарных последовательностей длины n, x = (x_1, x_2, \dots, x_n) \in \{ 0, 1 \}^n - произвольный элемент этого множества, S -  подмножество \{ 0, 1 \}^n. Обозначим через n_S количество элементов в множестве S и через n_S(x_1, x_2, \dots, x_k) - количество элементов в множестве S, первые k координат которых равны (x_1, x_2, \dots, x_k).

Под лексикографическим порядком будем понимать обычный словарный порядок, при котором 0 < 1, т.е. x < y если x_k < y_k для первого индекса k такого, что x_k \neq y_k.

Пример

00101 < 00110

Утверждение 1. Лексикографический индекс элемента x \in S рассчитывается по формуле

i_S(x) = \sum_{j=1}^{n} x_j n_S(x_1, x_2, \dots, x_{j-1}, 0)

Эта формула определяет лексикографическую биекцию S \rightarrow \{ 0, 1, \dots, n_S - 1 \}.

Доказательство

Слова с префиксом (x_1, x_2, \dots, x_{j-1}, 0) лексикографически предшествуют словам с префиксом (x_1, x_2, \dots, x_{j-1}, 1). Для каждого j такого, что x_j = 1, мы считаем количество n_S(x_1, \dots, x_{j-1}, 0) элементов S, которые впервые покоординатно отличаются от x в j-той координате и поэтому имеют меньший лексикографический индекс. Суммируя количество таких элементов для всех 1 \leq j \leq n, мы пересчитаем все элементы в S, у которых индекс меньше чем у x.

Утверждение 2. Пусть даны индекс i и множество S. Найти x такой, что i_S(x) = i можно с помощью следующего алгоритма:

  1. Если i \geq n_S(0), установить x_1 = 1, \: i = i - n_S(0); иначе установить x_1 = 0.

  2. Для всех k = 2, \dots, n, если i \geq n_S(x_1, \dots, x_{k-1}, 0), установить x_k = 1, \: i = i - n_S(x_1, \dots, x_{k-1}, 0); иначе установить x_k = 0.

Теперь рассмотрим бинарные последовательности с весом h:

S = \left\{ x \in \{ 0, 1\}^n: \sum_{j=1}^{n} x_j = h \right\}

В этом случае

n_S(x_1, x_2, \dots, x_{k-1}, 0) = \binom{n-k}{n(h, k)}

где

n(h, k) = h - \sum_{j=1}^{k-1} x_j

поскольку именно такое количество способов разместить оставшиеся n(h, k) единиц в оставшиеся n - k мест в последовательности. Следовательно,

i_S(x) = \sum_{k=1}^{n} x_k \binom{n-k}{n(h, k)}

Пример

Теперь вернемся к исходной задаче: нам необходимо преобразовать произвольную бинарную строку в сообщения длины p и веса  h. Для этого случая

n_S = \binom{p}{h}

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

\lfloor log_2 \binom{p}{h} \rfloor

, т.е. чтобы рассчитать количество бит для кодирования числа не больше n_S мы рассчитываем

log_2 \binom{p}{h}

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

При кодировании исходного бинарного сообщения, мы разбиваем его на блоки длины

\lfloor log_2 \binom{p}{h} \rfloor

бит каждый, и каждый такой блок рассматриваем как бинарное представление числа 0 \leq x \leq n_S. Для преобразование числа x в бинарный вектор \vec{y}, в соответствии с утверждением 2, можно использовать следующий алгоритм:

Алгоритм 2. Вход: x, p, h; Выход: \vec{y}

  1. для k \leftarrow 1 до p:

    1. если x \geq \binom{p-k}{h}, то

      1. y_k \leftarrow 1

      2. x \leftarrow x - \binom{p-k}{h}

      3. h \leftarrow h - 1

    2. иначе y_k \leftarrow 0

  2. вернуть \vec{y}

Обратное преобразование вектора \vec{y} в число x, в соответствии с утверждением 1, осуществляется по алгоритму:

Алгоритм 3. Вход: \vec{y}, p, h; Выход: x

  1. x \leftarrow 0

  2. для k \leftarrow 1 до p:

    1. если y_k = 1, то

      1. x \leftarrow x + \binom{p-k}{h}

      2. h \leftarrow h - 1

  3. вернуть x

Пример работы криптосистемы

Код

Пример был сгенерирован с помощью программы, исходный код которой расположен ниже. Для этого я использовал SageMath. Вы можете установить данное ПО локально по инструкции либо использовать платформу CoCalc. Я воспользовался вторым вариантом, так как на платформе CoCalc есть возможность создать Jupyter Notebook и использовать его бесплатно без доступа в интернет.

from sage.all import *
import numpy as np


# функция, рассчитывающая публичный ключ
def calculate_public_key(p, perm, d, g, t):
    # вычисляем дискретный логарифмы a_i
    a = []
    for i in range(p):
        a.append(discrete_log(t + i, g))
    
    # вычисляем c_i
    c = []
    for i in range(p):
        b = a[perm[i]]
        c.append((b + d) % g.multiplicative_order())
    
    return tuple(c)

# функция, преобразующая число x в вектор y длины p и веса h
def number_to_vector(x, p, h):
    # вычисляем максимальное по величине число, которое может быть
    # зашифровано: 2^(максимальная длина одного сообщения в битах)
    # проверяем, что x меньше максимального числа
    assert x < 2**int(log(binomial(p, h), 2).n())

    y = []

    for k in range(1, p+1):
        bin_coef = binomial(p-k, h)

        if x >= bin_coef:
            y.append(1)
            x = x - bin_coef
            h = h - 1
        else:
            y.append(0)

    return y

# функция зашифрования сообщения
def encrypt(m, c, p, h):
    M = number_to_vector(m, p, h)
    multiplicative_order = p**h - 1

    msg = 0
    for i in range(len(M)):
        if M[i] == 1:
            msg = (msg + c[i] ) % multiplicative_order

    return msg

# функция, преобразующая вектор y длины p и веса h в число x
def vector_to_number(y, p, h):
    x = 0

    for k in range(1, p+1):
        if y[k-1] == 1:
            x = x + binomial(p-k, h)
            h = h - 1

    return x

# функция расшифрования сообщения
def decrypt(msg, p, h, irreducible_poly, g, inverse_perm, d):
    multiplicative_order = p**h - 1
    c_ = (msg - h * d) % multiplicative_order
    u = g ** c_
    # получаем кольцо многочленов над полем GF(p)
    # т.е. коэффициенты многочленов принадлежат полю GF(p)
    R = PolynomialRing(GF(p), "t_")
    # складывает u и irreducible_poly как элементы кольца многочленов
    s = R(u.polynomial()) + R(irreducible_poly)

    M = [0] * p
    # перебираем все множители многочлена s (все линейные)
    for f in factor(s):
        # получаем корень
        coef = 0
        coefs = f[0].coefficients()
        if len(coefs) != 1:
            coef = int(coefs[0])
        
        # восстанавливаем соответствующий бит
        M[inverse_perm[coef]] = 1

    return vector_to_number(M, p, h)

# Генерация параметров криптосистемы

# p - характеристика, h - степень расширения поля
p = 11
h = 5

# определяем символическую переменную
x = var('x')
# неприводимый многочлен (для примера зафиксирован),
# по модулю которого строится поле GF(p^h)
irreducible_polynomial = x**5 + 9*x**3 + 9*x**2 + 3*x + 10

# создаем конечное поле p^h, modulus - неприводимый многочлен
F = GF(p**h, name="t", modulus=irreducible_polynomial)
irreducible_polynomial = F.modulus()
# корень неприводимого многочлена, по модулю
# которого построено поле F
t = F.gen()
# примитивный элемент поля (для примера зафиксирован)
g = 2*t**4 + 8*t**3 + 4*t**2 + 5*t**1 + 2

# случайная перестановка (для примера зафиксирована)
permutation = np.array([5, 6, 7, 2, 8, 10, 4, 0, 1, 3, 9])
# вычисляем обратную перестановку
inverse_permutation = np.arange(p)
inverse_permutation[permutation] = inverse_permutation.copy()

# выбираем случайное число d (для примера зафиксировано)
d = 84997

c = calculate_public_key(p, permutation, d, g, t)

m = 217
print(f"исходное сообщение: {m}")

# Шифрование
msg = encrypt(m, c, p, h)
print(f"зашифрованное сообщение: {msg}")

# Расшифрование
decrypted_msg = decrypt(
    msg, p, h,
    irreducible_polynomial, g,
    inverse_permutation, d,
)
print(f"расшифрованное сообщение: {decrypted_msg}")

Генерация параметров криптосистемы:

  1. Выбираем p = 11, h = 5.

  2. Выбираем неприводимый многочлен степени 5 над GF(11): f(t) = t^5 + 9t^3 + 9t^2 + 3t + 10. Элементы конечного поля GF(11^5) - многочлены степени не больше 5 с коэффициентами в GF(11), арифметические операции в котором выполняются по модулю f(t).

  3. Выбираем примитивный элемент g = 2t^4 + 8t^3 + 4t^2 + 5t + 2.

  4. Вычисляем дискретные логарифмы

a_0 = log_g(t) = 134600a_1 = log_g(t + 1) = 78964a_2 = log_g(t + 2) = 27756a_3 = log_g(t + 3) = 28213a_4 = log_g(t + 4) = 68089a_5 = log_g(t + 5) = 51642a_6 = log_g(t + 6) = 38125a_7 = log_g(t + 7) = 11604a_8 = log_g(t + 8) = 103713a_9 = log_g(t + 9) = 123616a_{10} = log_g(t + 10) = 15120

5. Выбираем случайную перестановку

\pi =         \begin{pmatrix}             0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 \\             5 & 6 & 7 & 2 & 8 & 10 & 4 & 0 & 1 & 3 & 9         \end{pmatrix}

т.е.

b_0 = a_{\pi(0)} = a_5b_1 = a_{\pi(1)} = a_6

и т.д.

6. Выбираем случайное число d = 84997 и вычисляем

c_0 = (b_0 + d) \: (\textrm{mod} \: p^h - 1) = (51642 + 84997) \: (\textrm{mod} \: 161050) = 136639c_1 = (b_1 + d) \: (\textrm{mod} \: p^h - 1) = (38125 + 84997) \: (\textrm{mod} \: 161050) = 123122c_2 = (b_2 + d) \: (\textrm{mod} \: p^h - 1) = (11604 + 84997) \: (\textrm{mod} \: 161050) = 96601c_3 = (b_3 + d) \: (\textrm{mod} \: p^h - 1) = (27756 + 84997) \: (\textrm{mod} \: 161050) = 112753c_4 = (b_4 + d) \: (\textrm{mod} \: p^h - 1) = (103713 + 84997) \: (\textrm{mod} \: 161050) = 27660c_5 = (b_5 + d) \: (\textrm{mod} \: p^h - 1) = (15120 + 84997) \: (\textrm{mod} \: 161050) = 100117c_6 = (b_6 + d) \: (\textrm{mod} \: p^h - 1) = (68089 + 84997) \: (\textrm{mod} \: 161050) = 153086c_7 = (b_7 + d) \: (\textrm{mod} \: p^h - 1) = (134600 + 84997) \: (\textrm{mod} \: 161050) = 58547c_8 = (b_8 + d) \: (\textrm{mod} \: p^h - 1) = (78964 + 84997) \: (\textrm{mod} \: 161050) = 2911c_9 = (b_9 + d) \: (\textrm{mod} \: p^h - 1) = (2911 + 84997) \: (\textrm{mod} \: 161050)  = 113210c_{10} = (b_{10} + d) \: (\textrm{mod} \: p^h - 1) = (123616 + 84997) \: (\textrm{mod} \: 161050) = 47563

Шифрование

Рассмотрим пример шифрования сообщения m = 217, или в бинарном виде: 11011001. Нам необходимо его преобразовать в бинарный вектор длины 11 и веса 5 в соответствии с алгоритмом 2. Его длина равна 8, т.е она не больше

 \lfloor log_2 \binom{p}{h} \rfloor = \lfloor log_2 \binom{11}{5} \rfloor = 8

, соответственно делить это сообщение необходимости нет.

  1. Используя алгоритм 2, преобразуем сообщение m в бинарный вектор M = (0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1).

  2. Далее складываем c_i, которые соответствуют битам, равным 1:

c = (c_1 + c_2 + c_4 + c_8 + c_{10}) \: (\textrm{mod} \: p^h - 1)  == (123122 + 96601 + 27660 + 2911 + 47563) \: (\textrm{mod} \: 161050) = 136807

c = 136807 - зашифрованное сообщение, которое будет отправлено.

Расшифрование

Чтобы расшифровать сообщение c = 136807:

  1. Вычисляем c' = c - hd \: (\textrm{mod} \: p^h-1) = (136807 - 5 * 84997) (\textrm{mod} \: 161050) = 33922.

  2. Вычисляем

u(t) = g^{c'} \: (\textrm{mod} \: f(t)) = (2t^4 + 8t^3 + 4t^2 + 5t + 2)^{33922} \: (\textrm{mod} \: t^5 + 9t^3 + 9t^2 + 3t + 10) == 9t^4 + 4t^3 + 7t^2 + 7t

3. Вычисляем

s(t) = u(t) + f(t) = (9t^4 + 4t^3 + 7t^2 + 7t) + (t^5 + 9t^3 + 9t^2 + 3t + 10) = = t^5 + 9t^4 + 2t^3 + 5t^2 + 10t + 10

4. Раскладываем s(t) на линейные множители:

s(t) = (t + 1) (t + 6) (t + 7) (t + 8) (t + 9)

5. Применяем обратную перестановку к полученным корням:

\pi^{-1}(1) = 8, \pi^{-1}(6) = 1, \pi^{-1}(7) = 2, \pi^{-1}(8) = 4, \pi^{-1}(9) = 10

следовательно, M = (0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1).

6. Используя алгоритм 3, восстанавливаем исходное сообщение m = 217.

Вот и все

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

Буду рад, если подпишетесь на мой профиль на Хабре и на личный блог: там я публикую свои статьи, проекты, новости и заметки об интересных технологиях. Также код из статьи доступен в GitHub-репозитории в формате Jupyter Notebook.