<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cryptography on kenji.blog</title><link>http://kenji.blog/ru/categories/cryptography/</link><description>Recent content in Cryptography on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/categories/cryptography/index.xml" rel="self" type="application/rss+xml"/><item><title>Основы и реализация криптографии с использованием малой теоремы Ферма</title><link>http://kenji.blog/ru/p/fermats-little-theorem-cryptography-implementation/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/fermats-little-theorem-cryptography-implementation/</guid><description>&lt;img src="http://kenji.blog/p/fermats-little-theorem-cryptography-implementation/img/eyecatch.jpg" alt="Featured image of post Основы и реализация криптографии с использованием малой теоремы Ферма" />&lt;h2 id="1-введение-тайна-математики-лежащая-в-основе-современной-криптографии">1. Введение: Тайна математики, лежащая в основе современной криптографии
&lt;/h2>&lt;p>В современном цифровом обществе, особенно в связи с передачей данных через Интернет, «криптография» стала неотъемлемой базовой технологией. То, что мы можем безопасно просматривать веб-сайты через HTTPS в веб-браузерах, совершать финансовые операции в онлайн-банкинге и приватно общаться в приложениях для обмена сообщениями, возможно благодаря криптографическим протоколам, работающим в фоновом режиме и опирающимся на продвинутую математическую теорию. Среди них особо важную роль играет «криптография с открытым ключом», ярким представителем которой является &lt;strong>алгоритм RSA&lt;/strong>.&lt;/p>
&lt;p>Безопасность и корректность многих криптографических алгоритмов, включая RSA, во многом зависят от очень красивой и мощной теоремы, открытой в 17 веке французским математиком Пьером де Ферма (Pierre de Fermat). Это &lt;strong>Малая теорема Ферма (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>. Кроме того, теорема Леонарда Эйлера (Leonhard Euler), которая является ее обобщением, также играет решающую роль в теории криптографии.&lt;/p>
&lt;p>В этой статье подробно и с самых основ объясняется, как открытие из области чистой математики, такое как малая теорема Ферма, применяется в современных практических криптографических технологиях, в частности, для «проверки на простоту» и в «алгоритме RSA». Это очень подробное техническое руководство, охватывающее математические доказательства, механизмы шифрования и дешифрования, а также конкретные реализации алгоритмов на C++ и Python.&lt;/p>
&lt;hr>
&lt;h2 id="2-основы-сравнений-и-модульной-арифметики">2. Основы сравнений и модульной арифметики
&lt;/h2>&lt;p>Чтобы понять малую теорему Ферма, сначала необходимо ознакомиться с математической концепцией «модульной арифметики (сравнений по модулю)». Модульная арифметика — это система вычислений, в которой основное внимание уделяется «остатку» от деления на определенное заданное число (называемое модулем). Поскольку вычисления похожи на циферблат часов (который делает полный оборот за 12 часов), ее также называют «арифметикой часов».&lt;/p>
&lt;p>Когда остатки от деления целых чисел $a$ и $b$ на положительное целое число $n$ равны, математически это записывается следующим образом:&lt;/p>
$$
a \equiv b \pmod n
$$
&lt;p>Это читается как «$a$ и $b$ сравнимы по модулю $n$». Например, остаток от деления 17 на 5 равен 2, и остаток от деления 12 на 5 также равен 2. Поэтому мы можем записать:&lt;/p>
$$
17 \equiv 12 \pmod 5 \equiv 2 \pmod 5
$$
&lt;p>В модульной арифметике обычные четыре арифметических действия (сложение, вычитание, умножение) работают так же:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Сложение&lt;/strong>: Если $a \equiv b \pmod n$ и $c \equiv d \pmod n$, то $a + c \equiv b + d \pmod n$&lt;/li>
&lt;li>&lt;strong>Вычитание&lt;/strong>: Если $a \equiv b \pmod n$ и $c \equiv d \pmod n$, то $a - c \equiv b - d \pmod n$&lt;/li>
&lt;li>&lt;strong>Умножение&lt;/strong>: Если $a \equiv b \pmod n$ и $c \equiv d \pmod n$, то $a \times c \equiv b \times d \pmod n$&lt;/li>
&lt;li>&lt;strong>Возведение в степень&lt;/strong>: Если $a \equiv b \pmod n$, то для любого натурального числа $k$ верно $a^k \equiv b^k \pmod n$&lt;/li>
&lt;/ol>
&lt;p>Однако с &lt;strong>делением&lt;/strong> следует быть осторожными. В общем случае, из того, что $a \times c \equiv b \times c \pmod n$, нельзя разделить обе части на $c$ и получить $a \equiv b \pmod n$. Это справедливо только в том случае, когда $c$ и $n$ взаимно просты (их наибольший общий делитель равен 1). Эта концепция «модульного обратного элемента» станет чрезвычайно важной при генерации ключей RSA, которая будет обсуждаться позже.&lt;/p>
&lt;hr>
&lt;h2 id="3-математические-основы-и-доказательство-малой-теоремы-ферма">3. Математические основы и доказательство малой теоремы Ферма
&lt;/h2>&lt;p>Теперь, когда мы усвоили основы модульной арифметики, давайте перейдем к главной теме — малой теореме Ферма.&lt;/p>
&lt;h3 id="31-определение-теоремы">3.1 Определение теоремы
&lt;/h3>&lt;p>Малая теорема Ферма формулируется следующим образом:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Малая теорема Ферма (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>
Пусть $p$ — простое число, а $a$ — любое целое число, не кратное $p$ (то есть $a$ и $p$ взаимно просты). Тогда справедливо следующее сравнение:
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>Также часто встречается форма, в которой условие «$a$ не кратно $p$» убирается, и теорема выражается в виде, справедливом для всех целых чисел $a$. В этом случае, если умножить обе части на $a$, мы получим:&lt;/p>
$$
a^p \equiv a \pmod p
$$
&lt;h3 id="32-проверка-на-конкретных-примерах">3.2 Проверка на конкретных примерах
&lt;/h3>&lt;p>Давайте проверим на конкретных числах, действительно ли работает эта теорема.
Пусть простое число $p = 5$. Тогда $p-1 = 4$. В качестве $a$ выберем целые числа, не кратные $p$.&lt;/p>
&lt;ul>
&lt;li>Для $a = 2$: $2^{5-1} = 2^4 = 16$. $16 \div 5 = 3$ с остатком $1$. Следовательно, $16 \equiv 1 \pmod 5$. (Выполняется)&lt;/li>
&lt;li>Для $a = 3$: $3^{5-1} = 3^4 = 81$. $81 \div 5 = 16$ с остатком $1$. Следовательно, $81 \equiv 1 \pmod 5$. (Выполняется)&lt;/li>
&lt;li>Для $a = 4$: $4^{5-1} = 4^4 = 256$. $256 \div 5 = 51$ с остатком $1$. Следовательно, $256 \equiv 1 \pmod 5$. (Выполняется)&lt;/li>
&lt;/ul>
&lt;p>Таким образом, какое бы $a$ мы ни выбрали (при условии, что оно не кратно 5), остаток от деления 4-й степени на 5 всегда будет равен 1. Это выглядит как магия, но проистекает из прекрасных свойств простых чисел.&lt;/p>
&lt;h3 id="33-математическое-доказательство-теоремы">3.3 Математическое доказательство теоремы
&lt;/h3>&lt;p>Почему же это справедливо? Здесь мы приведем элегантное доказательство с использованием множества классов вычетов.&lt;/p>
&lt;p>Рассмотрим множество $S = \{1, 2, 3, \dots, p-1\}$. Это представители целых чисел, остатки от деления которых на $p$ находятся в диапазоне от $1$ до $p-1$.
Теперь рассмотрим новое множество $T$, полученное умножением каждого элемента на целое число $a$, взаимно простое с $p$:
&lt;/p>
$$ T = \{1a, 2a, 3a, \dots, (p-1)a\} $$
&lt;p>Рассмотрим остатки от деления каждого элемента множества $T$ на $p$. Удивительно, но эти остатки (возможно, в другом порядке) полностью совпадают с множеством элементов исходного множества $S$.
Это происходит потому что:&lt;/p>
&lt;ol>
&lt;li>Никакой элемент $T$ не может быть кратен $p$ (так как ни $a$, ни исходные элементы не кратны $p$).&lt;/li>
&lt;li>В $T$ не существует двух различных элементов, сравнимых по модулю $p$. Если бы $ia \equiv ja \pmod p$ (при $i \neq j$), то, поскольку $a$ и $p$ взаимно просты, мы могли бы разделить обе части на $a$, получив $i \equiv j \pmod p$, что является противоречием.&lt;/li>
&lt;/ol>
&lt;p>Таким образом, произведение всех элементов множества $S$ сравнимо с произведением всех элементов множества $T$ по модулю $p$:&lt;/p>
$$
(1a) \times (2a) \times \dots \times ((p-1)a) \equiv 1 \times 2 \times \dots \times (p-1) \pmod p
$$
&lt;p>Если преобразовать левую часть, мы получим $p-1$ множителей $a$, поэтому:&lt;/p>
$$
a^{p-1} \cdot (p-1)! \equiv (p-1)! \pmod p
$$
&lt;p>Поскольку $(p-1)!$ взаимно просто с $p$, мы можем разделить обе части на $(p-1)!$, и в итоге выводится следующая теорема:&lt;/p>
$$
a^{p-1} \equiv 1 \pmod p
$$
&lt;p>Это и есть доказательство малой теоремы Ферма.&lt;/p>
&lt;hr>
&lt;h2 id="4-функция-эйлера-и-теорема-эйлера">4. Функция Эйлера и теорема Эйлера
&lt;/h2>&lt;p>Малая теорема Ферма касается «простого числа $p$», но Леонард Эйлер обобщил ее для «любого положительного целого числа $n$». Это расширение необходимо для понимания алгоритма RSA.&lt;/p>
&lt;h3 id="41-функция-эйлера-phin">4.1 Функция Эйлера $\phi(n)$
&lt;/h3>&lt;p>Функция Эйлера (также называемая тотиентом или $\phi$-функцией Эйлера) $\phi(n)$ представляет собой функцию, возвращающую «количество целых чисел от $1$ до $n$, взаимно простых с $n$».&lt;/p>
&lt;ul>
&lt;li>В случае простого числа $p$ все целые числа от $1$ до $p-1$ взаимно просты с $p$, поэтому $\phi(p) = p - 1$.&lt;/li>
&lt;li>Для двух различных простых чисел $p$ и $q$, в случае их произведения $n = p \times q$, $\phi(n)$ вычисляется по очень простой формуле:
$$ \phi(p \times q) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$&lt;/li>
&lt;/ul>
&lt;p>Это свойство становится фундаментальной логикой при генерации ключей RSA.&lt;/p>
&lt;h3 id="42-теорема-эйлера">4.2 Теорема Эйлера
&lt;/h3>&lt;p>Эйлер обобщил малую теорему Ферма следующим образом:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Теорема Эйлера (Euler&amp;rsquo;s Theorem)&lt;/strong>
Для любого положительного целого числа $n$ и взаимно простого с ним целого числа $a$ справедливо следующее:
&lt;/p>
$$ a^{\phi(n)} \equiv 1 \pmod n $$
&lt;/blockquote>
&lt;p>Если $n$ — простое число $p$, то $\phi(p) = p - 1$, и это становится самой малой теоремой Ферма ($a^{p-1} \equiv 1 \pmod p$). Иными словами, малая теорема Ферма является лишь частным случаем теоремы Эйлера.&lt;/p>
&lt;hr>
&lt;h2 id="5-поиск-огромных-простых-чисел-тест-ферма-на-простоту">5. Поиск огромных простых чисел: Тест Ферма на простоту
&lt;/h2>&lt;p>В криптографических технологиях (таких как алгоритм RSA или обмен ключами Диффи-Хеллмана) необходимо быстро находить «огромные простые числа», длина которых может составлять сотни цифр. Однако, чтобы определить, является ли огромное число $N$ простым с помощью метода «перебора делителей» от $2$ до $\sqrt{N}$, потребовалось бы время, сравнимое с возрастом Вселенной.&lt;/p>
&lt;p>И здесь на помощь приходит «вероятностный тест на простоту», использующий малую теорему Ферма наоборот. Он называется &lt;strong>тест Ферма (Fermat Primality Test)&lt;/strong>.&lt;/p>
&lt;h3 id="51-что-такое-вероятностный-тест-на-простоту">5.1 Что такое вероятностный тест на простоту
&lt;/h3>&lt;p>Согласно малой теореме Ферма, если $p$ — простое число, то для любого $a$ ($1 &lt; a &lt; p$) обязательно выполняется $a^{p-1} \equiv 1 \pmod p$.
Если взять контрапозицию, то можно сказать: «Если для некоторого $a$ выполняется $a^{p-1} \not\equiv 1 \pmod p$, то $p$ &lt;strong>абсолютно точно не является простым (является составным числом)&lt;/strong>».&lt;/p>
&lt;p>Следовательно, если мы хотим проверить, является ли число $N$ простым, мы случайно выбираем несколько чисел $a$, вычисляем $a^{N-1} \pmod N$ и проверяем, равно ли это $1$. Если хотя бы раз ответ будет отличным от $1$, то точно установлено, что $N$ — составное число. Если же мы пробуем много раз и всегда получаем $1$, то с высокой долей вероятности можно судить, что $N$ «вероятно, простое».&lt;/p>
&lt;h3 id="52-объяснение-алгоритма-и-блок-схема">5.2 Объяснение алгоритма и блок-схема
&lt;/h3>&lt;p>Алгоритм теста Ферма выглядит следующим образом:&lt;/p>
&lt;div class="mermaid">flowchart TD
Start["Начало"] --> Input["Ввести проверяемое число p и количество проверок k"]
Input --> LoopStart["Цикл от i = 0 до k-1"]
LoopStart --> Condition{"i &lt; k ?"}
Condition -- "Да" --> RandomA["Случайно выбрать целое число a в диапазоне 1 &lt; a &lt; p-1"]
RandomA --> Calc["Вычислить модульное возведение в степень a^(p-1) mod p"]
Calc --> CheckPrime{"Результат равен 1 ?"}
CheckPrime -- "Нет" --> ReturnComposite["p является составным (точно)"]
CheckPrime -- "Да" --> Increment["Увеличить i на 1"]
Increment --> Condition
Condition -- "Нет" --> ReturnPrime["p, вероятно, является простым (с высокой вероятностью)"]
ReturnComposite --> End["Конец"]
ReturnPrime --> End&lt;/div>
&lt;h3 id="53-подводный-камень-числа-кармайкла-псевдопростые">5.3 Подводный камень: числа Кармайкла (псевдопростые)
&lt;/h3>&lt;p>Тест Ферма очень быстр, но у него есть один существенный недостаток. Существуют такие дьявольские числа, которые, будучи составными, удовлетворяют условию $a^{N-1} \equiv 1 \pmod N$ для всех возможных $a$. Такие числа называются &lt;strong>числами Кармайкла (Carmichael numbers)&lt;/strong>. Наименьшим числом Кармайкла является $561$ ($3 \times 11 \times 17$).&lt;/p>
&lt;p>Из-за существования чисел Кармайкла только лишь с помощью теста Ферма нельзя абсолютно точно определить простоту числа. Поэтому в реальных криптографических системах (таких как OpenSSL) стандартно используется усовершенствованная версия теста Ферма — &lt;strong>тест простоты Миллера-Рабина (Miller-Rabin Primality Test)&lt;/strong>. Поскольку алгоритм Миллера-Рабина способен выявлять числа Кармайкла, вероятность ложного срабатывания может быть сведена практически к нулю.&lt;/p>
&lt;h3 id="54-быстрое-модульное-возведение-в-степень-метод-бинарного-возведения-в-степень">5.4 Быстрое модульное возведение в степень (метод бинарного возведения в степень)
&lt;/h3>&lt;p>В алгоритме проверки на простоту необходимо вычислять $a^{N-1} \pmod N$, но если $N$ огромно, то $a^{N-1}$ будет иметь астрономическое количество цифр и не поместится в память компьютера.
Решением этой проблемы является &lt;strong>метод бинарного возведения в степень (Exponentiation by Squaring)&lt;/strong>, или быстрое модульное возведение в степень. Путем взятия остатка (mod N) на каждом шаге вычисления значение всегда сохраняется меньше $N$, что позволяет выполнять вычисления очень быстро (со сложностью $O(\log N)$).&lt;/p>
&lt;hr>
&lt;h2 id="6-реализация-проверки-на-простоту-и-модульного-возведения-в-степень">6. Реализация проверки на простоту и модульного возведения в степень
&lt;/h2>&lt;p>Теперь давайте реализуем тест Ферма на простоту и метод бинарного возведения в степень на C++ и Python.&lt;/p>
&lt;h3 id="61-реализация-на-c">6.1 Реализация на C++
&lt;/h3>&lt;p>В C++ стандартные целочисленные типы подвержены переполнению, поэтому для работы с большими числами требуются библиотеки многократной точности (например, GMP), но здесь мы приведем реализацию в пределах 64-битных целых чисел (&lt;code>unsigned long long&lt;/code>) для понимания алгоритма.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;random&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Быстрое модульное возведение в степень (a^b mod m) - метод бинарного возведения в степень
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Если младший бит b равен 1, умножаем результат на a
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">result&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Расширение до 128 бит для предотвращения переполнения
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Возводим a в квадрат
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Сдвигаем b вправо (делим пополам)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">/=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Тест Ферма на простоту
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">iterations&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">random_device&lt;/span> &lt;span class="n">rd&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mt19937_64&lt;/span> &lt;span class="n">gen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uniform_int_distribution&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gen&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Если a^(p-1) mod p не равно 1, то это составное число
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Вероятно, простое
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Известное простое число
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is probably prime.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is composite.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-реализация-на-python">6.2 Реализация на Python
&lt;/h3>&lt;p>Стандартный целочисленный тип в Python поддерживает числа произвольной точности, поэтому не нужно беспокоиться о переполнении. Кроме того, встроенная функция Python &lt;code>pow(a, b, m)&lt;/code> внутренне использует метод бинарного возведения в степень и работает очень быстро.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Вероятностный тест на простоту с использованием теста Ферма
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">iterations&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Выбираем случайное число a от 2 до p-2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисляем a^(p-1) mod p. Встроенная функция pow работает быстро.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span> &lt;span class="c1"># Точно составное число&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span> &lt;span class="c1"># Вероятно, простое&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Тестирование&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">number_to_test&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">104729&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> вероятно, является простым.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> является составным.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-применение-в-алгоритме-rsa-место-встречи-ферма-и-эйлера">7. Применение в алгоритме RSA: Место встречи Ферма и Эйлера
&lt;/h2>&lt;p>Самым великим применением малой теоремы Ферма (и теоремы Эйлера) является &lt;strong>алгоритм RSA&lt;/strong>, разработанный в 1977 году Ривестом, Шамиром и Адлеманом.
RSA — это инновационная криптографическая система с «открытым ключом», в которой ключ для шифрования (открытый ключ) доступен всему миру, а ключ для дешифрования (закрытый ключ) известен только самому получателю.&lt;/p>
&lt;p>Эта асимметричность основана на вычислительной безопасности, обусловленной тем, что «разложение огромного составного числа на простые множители является чрезвычайно сложной задачей».&lt;/p>
&lt;h3 id="71-механизм-rsa-генерация-ключей-шифрование-дешифрование">7.1 Механизм RSA (генерация ключей, шифрование, дешифрование)
&lt;/h3>&lt;p>Давайте рассмотрим весь процесс обмена данными в алгоритме RSA с помощью диаграммы последовательности Mermaid.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Alice["Алиса (Получатель)"]
participant Bob["Боб (Отправитель)"]
Alice->>Alice: "Сгенерировать огромные простые числа p, q"
Alice->>Alice: "Вычислить N = p * q, φ(N) = (p-1)(q-1)"
Alice->>Alice: "Вычислить открытый ключ e и закрытый ключ d (e*d ≡ 1 mod φ(N))"
Alice->>Bob: "Отправить открытый ключ (N, e)"
Note over Bob: "Подготовить открытый текст M (M &lt; N)"
Bob->>Bob: "Вычислить шифротекст C = M^e mod N"
Bob->>Alice: "Отправить шифротекст C"
Alice->>Alice: "Вычислить открытый текст M = C^d mod N и расшифровать"&lt;/div>
&lt;p>Ниже описаны подробные математические шаги.&lt;/p>
&lt;h4 id="шаг-1-генерация-ключей-действия-получателя-алисы">Шаг 1: Генерация ключей (действия получателя Алисы)
&lt;/h4>&lt;ol>
&lt;li>Случайным образом генерируются два огромных простых числа $p$ и $q$ (здесь используется упомянутый ранее тест на простоту).&lt;/li>
&lt;li>Вычисляется их произведение $N = p \times q$. Это значение $N$ публикуется открыто.&lt;/li>
&lt;li>Используя функцию Эйлера, вычисляется $\phi(N) = (p-1)(q-1)$.&lt;/li>
&lt;li>Выбирается целое число $e$ (открытая экспонента), взаимно простое с $\phi(N)$ (часто используется $e = 65537$).&lt;/li>
&lt;li>Вычисляется модульный обратный элемент $d$ (закрытая экспонента) для $e$. То есть, находится такое $d$, которое удовлетворяет условию:
$$ e \cdot d \equiv 1 \pmod{\phi(N)} $$
Для этих вычислений используется &lt;strong>расширенный алгоритм Евклида&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>Таким образом, &lt;strong>открытым ключом становится пара $(N, e)$&lt;/strong>, а &lt;strong>закрытым ключом — пара $(N, d)$&lt;/strong>. (Значения $p, q, \phi(N)$ следует немедленно уничтожить или строго хранить в секрете).&lt;/p>
&lt;h4 id="шаг-2-шифрование-действия-отправителя-боба">Шаг 2: Шифрование (действия отправителя Боба)
&lt;/h4>&lt;p>Предположим, Боб хочет отправить Алисе сообщение $M$ (где $M$ — это преобразованный в числа текст, и $0 \le M &lt; N$).
Боб использует открытый ключ Алисы $(N, e)$ для вычисления шифротекста $C$ следующим образом:&lt;/p>
$$
C \equiv M^e \pmod N
$$
&lt;p>Затем этот $C$ передается Алисе по сети.&lt;/p>
&lt;h4 id="шаг-3-дешифрование-действия-получателя-алисы">Шаг 3: Дешифрование (действия получателя Алисы)
&lt;/h4>&lt;p>Получив шифротекст $C$, Алиса использует свой закрытый ключ $d$, который знает только она, для выполнения следующего вычисления:&lt;/p>
$$
M' \equiv C^d \pmod N
$$
&lt;p>Поразительно, но результат этого вычисления $M'$ полностью совпадает с исходным сообщением $M$.&lt;/p>
&lt;h3 id="72-почему-возможна-расшифровка-математическое-доказательство">7.2 Почему возможна расшифровка? (Математическое доказательство)
&lt;/h3>&lt;p>Здесь малая теорема Ферма (и теорема Эйлера) проявляет свою истинную ценность. Почему $C^d \pmod N$ превращается обратно в $M$?&lt;/p>
&lt;p>Давайте раскроем формулу дешифрования.
Поскольку $C \equiv M^e \pmod N$, то:
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>На шаге генерации ключей мы выбрали $d$ таким образом, что $e \cdot d \equiv 1 \pmod{\phi(N)}$. Это означает, что существует некоторое целое число $k$, при котором можно записать:
&lt;/p>
$$ e \cdot d = 1 + k \cdot \phi(N) $$
&lt;p>Подставим это в верхнюю формулу:
&lt;/p>
$$ M^{ed} = M^{1 + k \cdot \phi(N)} = M \cdot M^{k \cdot \phi(N)} = M \cdot (M^{\phi(N)})^k \pmod N $$
&lt;p>Здесь и появляется &lt;strong>теорема Эйлера&lt;/strong> ($M^{\phi(N)} \equiv 1 \pmod N$). (※Строго говоря, $M$ и $N$ должны быть взаимно простыми, но в RSA вероятность того, что $M$ и $N$ не взаимно просты, астрономически мала, и, используя китайскую теорему об остатках, можно доказать, что это будет работать, даже если они не взаимно просты).&lt;/p>
&lt;p>Применяя теорему Эйлера, так как $M^{\phi(N)} \equiv 1$, получаем:
&lt;/p>
$$ M \cdot (1)^k \equiv M \pmod N $$
&lt;p>$M$ чудесным образом восстановлено! Свойства чисел, открытые Ферма и Эйлером столетия назад, идеально гарантируют конфиденциальность современной цифровой связи.&lt;/p>
&lt;hr>
&lt;h2 id="8-игрушечная-реализация-алгоритма-rsa-python">8. Игрушечная реализация алгоритма RSA (Python)
&lt;/h2>&lt;p>Только в теории все может казаться сложным, поэтому давайте используем Python для фактической реализации процесса генерации ключей, шифрования и дешифрования RSA. Хотя это обучающая «игрушечная реализация», используемая математика абсолютно реальна.&lt;/p>
&lt;p>Мы также включим в реализацию «расширенный алгоритм Евклида» для нахождения модульного обратного элемента $d$.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Нахождение наибольшего общего делителя&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Расширенный алгоритм Евклида (находим x, y для ax + by = gcd(a,b))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Используется для нахождения d из e*d ≡ 1 (mod φ(N))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Обратного элемента не существует&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Функция генерации простых чисел (упрощенная: генерирует небольшие простые числа)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getrandbits&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Упрощенная проверка вместо упомянутого ранее теста Ферма&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">p&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Генерация ключей RSA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Убедимся, что p и q не одинаковые&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Для e часто используют простые числа, например 65537, но здесь выберем случайно&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисление закрытого ключа d&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Открытый ключ (e, n), Закрытый ключ (d, n)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисляем plaintext^e mod n&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cipher&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">cipher&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисляем cipher^d mod n и преобразуем обратно в символы&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">plain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">chr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plain&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример выполнения&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;--- Игрушечная реализация RSA ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">private_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Используем 12-битные простые числа&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Открытый ключ (e, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Закрытый ключ (d, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Hello Math!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">Исходное сообщение: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Шифрование&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">encrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Шифротекст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">encrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Дешифрование&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encrypted_msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Расшифрованное сообщение: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>При запуске этого кода вы увидите, как массив символов преобразуется в массив незнакомых чисел (шифротекст), а затем блестяще восстанавливается в исходную строку с помощью закрытого ключа.&lt;/p>
&lt;hr>
&lt;h2 id="9-заключение-место-пересечения-красоты-и-практичности-математики">9. Заключение: Место пересечения красоты и практичности математики
&lt;/h2>&lt;p>Когда в 17 веке Пьер де Ферма открыл эту «малую теорему», никто не мог подумать, что она когда-нибудь пригодится. Сам Ферма занимался теорией чисел исключительно из чистого математического любопытства.&lt;/p>
&lt;p>Однако почти 300 лет спустя, в 1970-х годах, на заре компьютерных сетей теорема Ферма совершила драматическое возрождение в качестве криптографической технологии, необходимой для создания безопасных протоколов связи. Метод проверки простоты, основанный на малой теореме Ферма, и алгоритм RSA, основанный на теореме Эйлера, буквально поддерживают инфраструктуру современного Интернета.&lt;/p>
&lt;p>И сообщения в мессенджерах, которые мы отправляем каждый день, и покупки на Amazon — все это танцует на фундаменте одной простой и красивой формулы: $a^{p-1} \equiv 1 \pmod p$. Малая теорема Ферма учит нас тому, что независимо от того, насколько абстрактной может быть математика, однажды обязательно наступит время, когда она послужит человечеству.&lt;/p>
&lt;p>При изучении программирования и теории криптографии понимание базовых математических структур станет для вас мощным инструментом. Это поможет глубоко понять поведение библиотек, предоставляемых в виде черных ящиков, и разрабатывать более безопасные системы.&lt;/p></description></item><item><title>Математическая интуиция криптографии на решетках (Lattice-based cryptography)</title><link>http://kenji.blog/ru/p/lattice-based-cryptography-math-intuition/</link><pubDate>Fri, 11 Sep 2026 21:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/lattice-based-cryptography-math-intuition/</guid><description>&lt;img src="http://kenji.blog/p/lattice-based-cryptography-math-intuition/img/eyecatch.jpg" alt="Featured image of post Математическая интуиция криптографии на решетках (Lattice-based cryptography)" />&lt;h1 id="1-введение-рассвет-постквантовой-криптографии-pqc-и-расцвет-криптографии-на-решетках">1. Введение: Рассвет постквантовой криптографии (PQC) и расцвет криптографии на решетках
&lt;/h1>&lt;p>Цифровая инфраструктура современного общества опирается на технологии криптографии с открытым ключом, такие как RSA и криптография на эллиптических кривых (ECC). Эти методы шифрования основывают свою безопасность на математической сложности таких задач, как &amp;ldquo;факторизация целых чисел&amp;rdquo; и &amp;ldquo;проблема дискретного логарифма&amp;rdquo;, которые, как считается, не могут быть эффективно решены (требуют экспоненциального времени) классическими компьютерами.&lt;/p>
&lt;p>Однако &amp;ldquo;алгоритм Шора&amp;rdquo;, опубликованный Питером Шором (Peter Shor) в 1994 году, потряс криптографический мир. Этот алгоритм математически доказал, что когда будут созданы крупномасштабные квантовые компьютеры, они смогут решать проблемы факторизации и дискретного логарифма за полиномиальное время. Это означает, что широко используемая сегодня криптография с открытым ключом в будущем станет полностью уязвимой для расшифровки.&lt;/p>
&lt;p>Чтобы противостоять этой &amp;ldquo;квантовой угрозе&amp;rdquo; (Quantum Threat), возникла острая необходимость в исследовании новых методов шифрования, которые трудно взломать даже с помощью квантовых компьютеров. Эта область называется &amp;ldquo;постквантовой криптографией&amp;rdquo; (Post-Quantum Cryptography: PQC).&lt;/p>
&lt;p>В PQC существует несколько перспективных кандидатов, таких как криптография на основе хешей, криптография на основе кодов, многомерная полиномиальная криптография и криптография на основе изогений. Однако &amp;ldquo;криптография на решетках&amp;rdquo; (Lattice-based cryptography) в настоящее время привлекает наибольшее внимание и является центральным элементом процесса стандартизации PQC Национальным институтом стандартов и технологий США (NIST). По сравнению с другими методами, криптография на решетках обладает чрезвычайно высокой скоростью шифрования и дешифрования, а также выдающейся особенностью: она имеет очень сильное доказательство безопасности в криптографии — сведение от &amp;ldquo;сложности в худшем случае&amp;rdquo; (Worst-case complexity) к &amp;ldquo;сложности в среднем случае&amp;rdquo; (Average-case complexity).&lt;/p>
&lt;p>В этой статье мы подробно рассмотрим криптографию на решетках, начиная с математического определения &amp;ldquo;решетки&amp;rdquo; (Lattice), которая является ее основой. Мы обсудим сложные задачи на решетках, такие как SVP (задача поиска кратчайшего вектора) и CVP (задача поиска ближайшего вектора), а также проблему &amp;ldquo;LWE (Learning With Errors)&amp;rdquo;, которая является сердцем современной криптографии на решетках. Будут представлены математические формулы, геометрическая интуиция и конкретные числовые примеры.&lt;/p>
&lt;h1 id="2-математическое-определение-решетки-lattice-и-геометрическая-интуиция">2. Математическое определение решетки (Lattice) и геометрическая интуиция
&lt;/h1>&lt;h2 id="21-векторные-пространства-и-решетки">2.1 Векторные пространства и решетки
&lt;/h2>&lt;p>В математике &amp;ldquo;решетка&amp;rdquo; (Lattice) — это множество дискретных точек, регулярно расположенных в $n$-мерном действительном векторном пространстве $\mathbb{R}^n$. Оно похоже на векторное пространство (Vector Space), изучаемое в линейной алгебре, но есть одно решающее отличие. В то время как векторное пространство — это непрерывное пространство, представленное линейными комбинациями базисных векторов с &amp;ldquo;действительными коэффициентами&amp;rdquo;, решетка — это дискретное пространство, представленное линейными комбинациями базисных векторов с &amp;ldquo;целыми коэффициентами&amp;rdquo;.&lt;/p>
&lt;p>Дадим строгое математическое определение. Рассмотрим $n$ ($n \le m$) линейно независимых векторов $\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n$ в $m$-мерном действительном векторном пространстве $\mathbb{R}^m$. Пусть матрица, содержащая эти векторы в качестве столбцов, будет $B = [\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n] \in \mathbb{R}^{m \times n}$. Эта матрица $B$ называется &amp;ldquo;базисом&amp;rdquo; (Basis) решетки.&lt;/p>
&lt;p>Решетка $\mathcal{L}(B)$, порожденная этим базисом $B$, определяется следующим образом:&lt;/p>
$$
\mathcal{L}(B) = \left\{ \sum_{i=1}^{n} x_i \mathbf{b}_i \mathrel{\bigg|} x_i \in \mathbb{Z} \right\} = \{ B \mathbf{x} \mid \mathbf{x} \in \mathbb{Z}^n \}
$$
&lt;p>Важно отметить, что коэффициенты $x_i$ ограничены целыми числами $\mathbb{Z}$, а не действительными числами $\mathbb{R}$. В результате образуется &amp;ldquo;множество дискретных точек&amp;rdquo;, подобных равномерно расположенным перекресткам, а не непрерывное пространство с бесконечным множеством точек.&lt;/p>
&lt;h2 id="22-геометрический-образ">2.2 Геометрический образ
&lt;/h2>&lt;p>Рассмотрим пример в двумерной плоскости $\mathbb{R}^2$. Если мы выберем в качестве базисных векторов $\mathbf{b}_1 = \begin{pmatrix} 1 \\ 0 \end{pmatrix}$ и $\mathbf{b}_2 = \begin{pmatrix} 0 \\ 1 \end{pmatrix}$, то порожденная ими решетка будет представлять собой множество всех целочисленных координат $(x, y) \in \mathbb{Z}^2$ на координатной плоскости. Это самая простая &amp;ldquo;квадратная решетка&amp;rdquo;.&lt;/p>
&lt;p>Однако решетки не всегда ортогональны. Например, если мы рассмотрим базис $\mathbf{b}_1 = \begin{pmatrix} 2 \\ 1 \end{pmatrix}$ и $\mathbf{b}_2 = \begin{pmatrix} 1 \\ 3 \end{pmatrix}$, то порожденные точки будут напоминать пересечения диагонально искаженной сетки.&lt;/p>
&lt;h2 id="23-неоднозначность-базиса-и-унимодулярные-преобразования">2.3 Неоднозначность базиса и унимодулярные преобразования
&lt;/h2>&lt;p>Существует важное свойство, касающееся основы безопасности криптографии на решетках: &amp;ldquo;Существует бесконечное количество базисов, порождающих одну и ту же решетку&amp;rdquo;.&lt;/p>
&lt;p>Например, базис $\mathbf{b}_1 = (1, 0)^T, \mathbf{b}_2 = (0, 1)^T$, упомянутый ранее, порождает решетку $\mathbb{Z}^2$. Базис $\mathbf{b}'_1 = (1, 1)^T, \mathbf{b}'_2 = (2, 3)^T$ порождает абсолютно ту же самую решетку $\mathbb{Z}^2$.&lt;/p>
&lt;p>Необходимым и достаточным условием того, что базис $B$ и другой базис $B'$ порождают одну и ту же решетку, является существование матрицы с целочисленными элементами $U \in \mathbb{Z}^{n \times n}$, определитель которой $\det(U) = \pm 1$, так что:
&lt;/p>
$$ B' = B U $$
&lt;p>
Такая матрица $U$ называется &amp;ldquo;унимодулярной матрицей&amp;rdquo; (Unimodular matrix).&lt;/p>
&lt;p>Основная идея применения в криптографии состоит в том, чтобы использовать &amp;ldquo;хороший базис&amp;rdquo; (почти ортогональный, состоящий из коротких векторов) в качестве секретного ключа, и &amp;ldquo;плохой базис&amp;rdquo; (состоящий из очень длинных векторов, сильно скошенных друг относительно друга) в качестве открытого ключа. Вычислить хороший базис из плохого становится крайне сложно по мере увеличения размерности. В этом заключается базовая интуиция криптографии на решетках.&lt;/p>
&lt;h1 id="3-вычислительно-сложные-задачи-на-решетках">3. Вычислительно сложные задачи на решетках
&lt;/h1>&lt;p>Безопасность криптографии на решетках зависит от сложности решения определенных математических задач на решетках. Здесь мы представим две самые фундаментальные и известные проблемы.&lt;/p>
&lt;h2 id="31-задача-поиска-кратчайшего-вектора-shortest-vector-problem-svp">3.1 Задача поиска кратчайшего вектора (Shortest Vector Problem: SVP)
&lt;/h2>&lt;p>SVP — это самая классическая и известная задача в теории решеток.&lt;/p>
&lt;p>&lt;strong>Определение (SVP):&lt;/strong>
Дан произвольный базис решетки $B$. Найдите в решетке $\mathcal{L}(B)$ ненулевой вектор $\mathbf{v}$, евклидова норма (длина) которого минимальна.&lt;/p>
&lt;p>Математически это задача поиска вектора $\mathbf{v}$, такого что достигается минимум $\min_{\mathbf{v} \in \mathcal{L}(B) \setminus \{\mathbf{0}\}} \| \mathbf{v} \|$. Эта минимальная длина обозначается как $\lambda_1(\mathcal{L})$ и называется &amp;ldquo;первым последовательным минимумом решетки&amp;rdquo;.&lt;/p>
&lt;p>В низких измерениях, таких как 2D или 3D, можно нарисовать график и визуально найти самый короткий вектор, или эффективно решить задачу с помощью алгоритма редукции решетки Гаусса. Однако известно, что когда размерность $n$ достигает сотен или тысяч, точное решение SVP является NP-трудным.&lt;/p>
&lt;p>В реальной криптографии используется приближенная SVP ($\gamma$-SVP), которая находит &amp;ldquo;приближенно короткий вектор&amp;rdquo;, а не строго кратчайший вектор. Когда коэффициент аппроксимации $\gamma$ полиномиального размера, эта проблема все еще считается чрезвычайно сложной.&lt;/p>
&lt;h2 id="32-задача-поиска-ближайшего-вектора-closest-vector-problem-cvp">3.2 Задача поиска ближайшего вектора (Closest Vector Problem: CVP)
&lt;/h2>&lt;p>CVP также является чрезвычайно важной задачей в криптографии на решетках.&lt;/p>
&lt;p>&lt;strong>Определение (CVP):&lt;/strong>
Дан произвольный базис решетки $B$ и любой целевой вектор $\mathbf{t} \in \mathbb{R}^m$ в пространстве (не обязательно являющийся точкой решетки). Найдите точку решетки $\mathbf{v} \in \mathcal{L}(B)$, ближайшую к $\mathbf{t}$.&lt;/p>
&lt;p>Математически это задача поиска точки решетки $\mathbf{v}$, такой что достигается минимум $\min_{\mathbf{v} \in \mathcal{L}(B)} \| \mathbf{v} - \mathbf{t} \|$.&lt;/p>
&lt;p>Как и SVP, CVP является NP-трудной в высоких измерениях. С точки зрения применения в криптографии, проблема LWE, которая будет обсуждаться позже, тесно связана с особым вариантом этой CVP (Bounded Distance Decoding: BDD).&lt;/p>
&lt;h2 id="33-почему-это-не-решается-в-высоких-размерностях-пределы-lll-и-bkz">3.3 Почему это не решается в высоких размерностях? (Пределы LLL и BKZ)
&lt;/h2>&lt;p>Известным алгоритмом для решения задач на решетках высокой размерности является алгоритм LLL (алгоритм Ленстры-Ленстры-Ловаса). Алгоритм LLL работает за полиномиальное время и может в некоторой степени &amp;ldquo;редуцировать&amp;rdquo; базис решетки до &amp;ldquo;хорошего базиса&amp;rdquo;. Однако кратчайший вектор, который может найти алгоритм LLL, имеет коэффициент аппроксимации, который экспоненциально зависит от размерности ($2^{\mathcal{O}(n)}$) по сравнению с истинным кратчайшим вектором, что недостаточно для нарушения безопасности криптографии.&lt;/p>
&lt;p>С помощью более мощных алгоритмов редукции базиса, таких как алгоритм BKZ (Block Korkine-Zolotarev), который является улучшением LLL, можно найти более короткие векторы, но вычислительная сложность возрастает экспоненциально в зависимости от размера блока. В криптографии на решетках безопасные параметры (например, значение размерности $n$) определяются путем оценки времени выполнения этого алгоритма BKZ. В текущих стандартных параметрах PQC размерность $n$ выбирается от 500 до 1000 и более, и считается, что для взлома потребуется время, превышающее возраст Вселенной, даже при использовании суперкомпьютеров или будущих квантовых компьютеров.&lt;/p>
&lt;h1 id="4-математическая-формулировка-проблемы-lwe-learning-with-errors">4. Математическая формулировка проблемы LWE (Learning With Errors)
&lt;/h1>&lt;p>Большая часть современной криптографии на решетках основана на проблеме &amp;ldquo;LWE (Learning With Errors)&amp;rdquo;, предложенной Одедом Регевом (Oded Regev) в 2005 году. Красота проблемы LWE заключается в простоте ее формулировки и мощном математическом доказательстве &amp;ldquo;сведения от сложности в худшем случае к сложности в среднем случае&amp;rdquo;.&lt;/p>
&lt;h2 id="41-система-линейных-уравнений-без-шума">4.1 Система линейных уравнений без шума
&lt;/h2>&lt;p>Чтобы понять проблему LWE, давайте сначала рассмотрим простую систему линейных уравнений без шума.
Предположим, у нас есть неизвестный секретный вектор $\mathbf{s} \in \mathbb{Z}_q^n$ (где каждая компонента - целое число от $0$ до $q-1$). Здесь $q$ - простое число.&lt;/p>
&lt;p>Мы выбираем случайные векторы коэффициентов $\mathbf{a}_1, \mathbf{a}_2, \dots \in \mathbb{Z}_q^n$ и вычисляем их скалярное произведение с секретным вектором $\mathbf{s}$ по модулю $q$.
$b_1 = \langle \mathbf{a}_1, \mathbf{s} \rangle \pmod q$
$b_2 = \langle \mathbf{a}_2, \mathbf{s} \rangle \pmod q$
$\vdots$&lt;/p>
&lt;p>Учитывая достаточное количество (не менее $n$) пар $(\mathbf{a}_i, b_i)$, мы можем легко восстановить секретный вектор $\mathbf{s}$, используя &amp;ldquo;метод исключения Гаусса&amp;rdquo; из линейной алгебры. Эту задачу легко решить за полиномиальное время.&lt;/p>
&lt;h2 id="42-определение-проблемы-lwe-добавление-шума">4.2 Определение проблемы LWE: Добавление шума
&lt;/h2>&lt;p>Что произойдет, если мы добавим немного &amp;ldquo;шума (ошибки)&amp;rdquo; в эту задачу?
В этом суть проблемы LWE.&lt;/p>
&lt;p>Для неизвестного секретного вектора $\mathbf{s} \in \mathbb{Z}_q^n$, мы добавляем небольшую ошибку $e_i \in \mathbb{Z}_q$ к результату каждого уравнения.
$b_i = \langle \mathbf{a}_i, \mathbf{s} \rangle + e_i \pmod q$&lt;/p>
&lt;p>Здесь $e_i$ - небольшое целое число со средним значением 0 и относительно небольшим стандартным отклонением (например, выбранное из дискретного распределения Гаусса).
Предоставляемая информация представляет собой список пар случайных векторов $\mathbf{a}_i$ и $b_i$, вычисленных с добавлением этой ошибки.
$( \mathbf{a}_1, b_1 ), ( \mathbf{a}_2, b_2 ), \dots, ( \mathbf{a}_m, b_m )$&lt;/p>
&lt;p>Это можно очень элегантно выразить в матричной форме.
Используя случайную матрицу $A \in \mathbb{Z}_q^{m \times n}$, секретный вектор $\mathbf{s} \in \mathbb{Z}_q^n$ и вектор ошибки $\mathbf{e} \in \mathbb{Z}_q^m$, мы можем записать:
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q $$
&lt;p>
Нам даны только $A$ и $\mathbf{b}$. Процесс поиска $\mathbf{s}$ на их основе называется &amp;ldquo;проблемой поиска LWE&amp;rdquo; (Search LWE problem).&lt;/p>
&lt;p>Поскольку в ней присутствует ошибка $e_i$, попытка использовать метод исключения Гаусса приведет к экспоненциальному росту ошибки в процессе сложения и вычитания уравнений, делая невозможным получение правильного ответа. На первый взгляд это выглядит как простая система линейных уравнений, но добавление этого небольшого шума повышает уровень сложности задачи до NP-трудной.&lt;/p>
&lt;h2 id="43-проблема-решения-lwe-decision-lwe">4.3 Проблема решения LWE (Decision LWE)
&lt;/h2>&lt;p>В криптографических доказательствах часто используется вариация задачи поиска LWE, называемая &amp;ldquo;проблемой решения LWE&amp;rdquo; (Decision LWE problem).&lt;/p>
&lt;p>Задача решения LWE состоит в том, чтобы, получив список образцов из следующих двух распределений, определить, из какого распределения они получены:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Распределение LWE&lt;/strong>: Намеренно вычисленное $(A, \mathbf{b} = A\mathbf{s} + \mathbf{e} \pmod q)$&lt;/li>
&lt;li>&lt;strong>Равномерное случайное распределение&lt;/strong>: $(A, \mathbf{u})$, состоящее из совершенно случайно выбранной матрицы $A$ и вектора $\mathbf{u}$&lt;/li>
&lt;/ol>
&lt;p>Удивительно, но если параметры проблемы LWE выбраны правильно, пары, полученные из распределения LWE, становятся &amp;ldquo;вычислительно неразличимыми&amp;rdquo; (Computationally Indistinguishable) от полностью случайных пар данных. Это свойство является основой того, что криптография на базе LWE может генерировать &amp;ldquo;шифротексты, неотличимые от случайных чисел&amp;rdquo;.&lt;/p>
&lt;h2 id="44-сведение-от-сложности-в-худшем-случае-к-сложности-в-среднем-случае-теорема-регева">4.4 Сведение от сложности в худшем случае к сложности в среднем случае (Теорема Регева)
&lt;/h2>&lt;p>Величайшим достижением Одеда Регева было математическое связывание сложности этой проблемы LWE со сложностью вышеупомянутых задач на решетках (SVP и CVP).&lt;/p>
&lt;p>Используя квантовое сведение (Quantum reduction), он доказал, что: &amp;ldquo;Если существует алгоритм полиномиального времени, который может решить проблему LWE в среднем (для случайно выбранных $A$ и $\mathbf{e}$), то существует квантовый алгоритм полиномиального времени, который может решить Gap-SVP в худшем случае (самый сложный случай) для любой решетки&amp;rdquo;. (Позже Пейкерт (Peikert) и другие также продемонстрировали классическое сведение).&lt;/p>
&lt;p>Это свойство, похожее на мечту в криптографии. Причина в том, что оно устраняет опасение: &amp;ldquo;Шифр может быть взломан, потому что мы случайно выбрали слабый ключ (часть среднего случая)&amp;rdquo;, и дает сильную гарантию: &amp;ldquo;Если мы можем решить LWE в среднем, то мы можем решить все сложные задачи на решетках (поэтому LWE абсолютно сложна)&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
A["Худший случай задач на решетках (Gap-SVP, SIVP)"] -->|Квантовое/Классическое сведение| B["Проблема LWE в среднем случае"]
B -->|Криптографическая конструкция| C["Криптосистемы на основе LWE (PKE, KEM, FHE)"]
style A fill:#ffcccc,stroke:#ff0000,stroke-width:2px,color:#000
style B fill:#ccffcc,stroke:#00aa00,stroke-width:2px,color:#000
style C fill:#ccccff,stroke:#0000ff,stroke-width:2px,color:#000&lt;/div>
&lt;h1 id="5-построение-криптографии-с-открытым-ключом-с-использованием-lwe-криптосистема-регева">5. Построение криптографии с открытым ключом с использованием LWE (Криптосистема Регева)
&lt;/h1>&lt;p>Поняв сложность проблемы LWE, давайте посмотрим на базовую криптосистему с открытым ключом, предложенную Одедом Регевом, чтобы увидеть, как с ее помощью выполняются шифрование и дешифрование. Здесь мы объясним самый основной механизм шифрования 1-битного сообщения $M \in \{0, 1\}$.&lt;/p>
&lt;h2 id="51-генерация-ключей-key-generation">5.1 Генерация ключей (Key Generation)
&lt;/h2>&lt;ol>
&lt;li>Определяются системные параметры: простое число $q$, размерность $n$ и количество уравнений $m$ ($m > n \log q$).&lt;/li>
&lt;li>В качестве секретного ключа случайно выбирается вектор $\mathbf{s} \in \mathbb{Z}_q^n$.&lt;/li>
&lt;li>Генерируется случайная матрица $A \in \mathbb{Z}_q^{m \times n}$.&lt;/li>
&lt;li>Малый вектор ошибки $\mathbf{e} \in \mathbb{Z}_q^m$ выбирается из распределения ошибок, например, дискретного гауссовского.&lt;/li>
&lt;li>Вычисляется вектор $\mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q$.&lt;/li>
&lt;li>Открытый ключ (Public Key) — это $(A, \mathbf{b})$.&lt;/li>
&lt;li>Секретный ключ (Secret Key) — это $\mathbf{s}$.&lt;/li>
&lt;/ol>
&lt;p>Открытый ключ - это буквально &amp;ldquo;экземпляр проблемы LWE&amp;rdquo;. Поиск секретного ключа $\mathbf{s}$ по открытому ключу $(A, \mathbf{b})$ эквивалентен решению задачи поиска LWE, поэтому безопасность гарантирована.&lt;/p>
&lt;h2 id="52-шифрование-encryption">5.2 Шифрование (Encryption)
&lt;/h2>&lt;p>Алиса использует открытый ключ Боба $(A, \mathbf{b})$ для шифрования 1-битного сообщения $M \in \{0, 1\}$.&lt;/p>
&lt;ol>
&lt;li>Выбирается случайный бинарный вектор (с компонентами 0 или 1) $\mathbf{r} \in \{0, 1\}^m$.&lt;/li>
&lt;li>В качестве первой части шифротекста вычисляется вектор $\mathbf{u} = A^T \mathbf{r} \pmod q$. ($A^T$ - транспонированная матрица $A$. Это означает, что мы суммируем те строки $A$, для которых компоненты $\mathbf{r}$ равны 1).&lt;/li>
&lt;li>В качестве второй части шифротекста вычисляется скаляр $v = \mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q$.
(Если сообщение $M$ равно 0, ничего не добавляется; если $1$, добавляется ровно половина значения $q$, то есть $\lfloor \frac{q}{2} \rfloor$).&lt;/li>
&lt;li>Шифротекст (Ciphertext) представляет собой $(\mathbf{u}, v)$.&lt;/li>
&lt;/ol>
&lt;p>Интуитивный смысл шифрования заключается во взятии &amp;ldquo;суммы случайного подмножества&amp;rdquo; для матрицы открытого ключа $A$ и вектора $\mathbf{b}$. Из-за сложности проблемы решения LWE этот шифротекст $(\mathbf{u}, v)$ выглядит неотличимым от полностью случайного вектора и равномерного случайного числа (семантическая безопасность: Semantic Security).&lt;/p>
&lt;div class="mermaid">flowchart LR
M["Сообщение M в {0,1}"] --> Enc
PK["Открытый ключ (A, b)"] --> Enc
r["Случайный бинарный вектор r"] --> Enc
subgraph Enc ["Процесс шифрования"]
direction TB
u_calc["u = A^T * r mod q"]
v_calc["v = b^T * r + M * floor(q/2) mod q"]
end
Enc --> CT["Шифротекст (u, v)"]&lt;/div>
&lt;h2 id="53-дешифрование-decryption">5.3 Дешифрование (Decryption)
&lt;/h2>&lt;p>Боб расшифровывает шифротекст $(\mathbf{u}, v)$, используя свой секретный ключ $\mathbf{s}$.&lt;/p>
&lt;ol>
&lt;li>Он вычисляет следующее значение: $D = v - \mathbf{s}^T \mathbf{u} \pmod q$&lt;/li>
&lt;li>Если вычисленный результат ближе к $0$, выводится $M=0$; если он ближе к $\lfloor \frac{q}{2} \rfloor$, выводится $M=1$.&lt;/li>
&lt;/ol>
&lt;p>Давайте математически раскроем это, чтобы понять, почему оно может быть расшифровано.
Вспомните, что $\mathbf{b} = A \mathbf{s} + \mathbf{e}$.&lt;/p>
$$
\begin{aligned}
v - \mathbf{s}^T \mathbf{u} &amp;= (\mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T (A^T \mathbf{r}) \\
&amp;= ((A \mathbf{s} + \mathbf{e})^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= (\mathbf{s}^T A^T \mathbf{r} + \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q
\end{aligned}
$$
&lt;p>Здесь $\mathbf{s}^T A^T \mathbf{r}$ аккуратно компенсируется и исчезает из уравнения!
Остается только $\mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor$.&lt;/p>
&lt;p>$\mathbf{e}$ - это вектор шума с очень маленькими компонентами, а $\mathbf{r}$ - бинарный вектор с компонентами 0 или 1. Следовательно, их скалярное произведение $\mathbf{e}^T \mathbf{r}$ также останется относительно небольшим значением (если параметры выбраны правильно).&lt;/p>
&lt;ul>
&lt;li>Если $M=0$, то результат будет равен $\mathbf{e}^T \mathbf{r}$, что является малым значением, близким к $0$.&lt;/li>
&lt;li>Если $M=1$, то результат будет равен $\mathbf{e}^T \mathbf{r} + \lfloor \frac{q}{2} \rfloor$, что будет расположено вокруг половины значения $q$, то есть $\lfloor \frac{q}{2} \rfloor$.&lt;/li>
&lt;/ul>
&lt;p>Если параметры спроектированы так, что абсолютное значение ошибки $\mathbf{e}^T \mathbf{r}$ находится в пределах $\frac{q}{4}$, Боб может точно определить (расшифровать) сообщение $M$, просто посмотрев, к чему ближе результат вычислений: к $0$ или к $\lfloor \frac{q}{2} \rfloor$. Это прекрасный механизм, благодаря которому работает криптография на базе LWE.&lt;/p>
&lt;div class="mermaid">flowchart LR
CT["Шифротекст (u, v)"] --> Dec
SK["Секретный ключ s"] --> Dec
subgraph Dec ["Процесс дешифрования"]
direction TB
calc["Вычислить D = v - s^T * u mod q"]
check["Проверить, ближе ли D к 0 или q/2"]
end
calc --> check
Dec --> M_out["Восстановленное сообщение M"]&lt;/div>
&lt;h1 id="6-игрушечный-пример-lwe-шифрования-с-конкретными-числами">6. Игрушечный пример LWE-шифрования с конкретными числами
&lt;/h1>&lt;p>Я думаю, что трудно прочувствовать это только с помощью ряда формул, поэтому давайте фактически проследим вычисления от шифрования до дешифрования, установив очень маленькие числовые параметры.
(&lt;em>В реальных криптографических системах для обеспечения безопасности используются значения $n$ от 500 и выше, и $q$ от нескольких тысяч и выше&lt;/em>)&lt;/p>
&lt;p>&lt;strong>[Установка параметров]&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Модуль $q = 17$ (Простое число. Таким образом, значения принимаются в диапазоне от $0$ до $16$)&lt;/li>
&lt;li>Размерность $n = 2$&lt;/li>
&lt;li>Количество уравнений $m = 4$&lt;/li>
&lt;li>Предположим, мы шифруем сообщение $M = 1$.&lt;/li>
&lt;li>Величина сдвига сообщения: $\lfloor \frac{q}{2} \rfloor = \lfloor \frac{17}{2} \rfloor = 8$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[1. Фаза генерации ключей]&lt;/strong>
Боб случайным образом выбирает секретный ключ $\mathbf{s}$, матрицу $A$ и вектор ошибки $\mathbf{e}$.
&lt;/p>
$$ \mathbf{s} = \begin{pmatrix} 3 \\ 4 \end{pmatrix} \in \mathbb{Z}_{17}^2 $$
$$ A = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \in \mathbb{Z}_{17}^{4 \times 2} $$
$$ \mathbf{e} = \begin{pmatrix} 1 \\ -1 \\ 0 \\ 2 \end{pmatrix} \equiv \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} \pmod{17} $$
&lt;p>Далее вычисляется открытый ключ $\mathbf{b}$.
&lt;/p>
$$ A \mathbf{s} = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \begin{pmatrix} 3 \\ 4 \end{pmatrix} = \begin{pmatrix} 2\times 3 + 15\times 4 \\ 1\times 3 + 8\times 4 \\ 14\times 3 + 5\times 4 \\ 9\times 3 + 10\times 4 \end{pmatrix} = \begin{pmatrix} 6 + 60 \\ 3 + 32 \\ 42 + 20 \\ 27 + 40 \end{pmatrix} = \begin{pmatrix} 66 \\ 35 \\ 62 \\ 67 \end{pmatrix} $$
&lt;p>
Мы вычисляем это по модулю 17. (Например, $66 = 17 \times 3 + 15$)
&lt;/p>
$$ A \mathbf{s} \pmod{17} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} $$
&lt;p>
Мы прибавляем вектор ошибки $\mathbf{e}$.
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} + \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} = \begin{pmatrix} 16 \\ 17 \\ 11 \\ 18 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 0 \\ 11 \\ 1 \end{pmatrix} \pmod{17} $$
&lt;p>Открытый ключ состоит из $A$ и $\mathbf{b} = (16, 0, 11, 1)^T$.&lt;/p>
&lt;p>&lt;strong>[2. Фаза шифрования]&lt;/strong>
Алиса шифрует сообщение $M = 1$.
Она выбирает случайный вектор $\mathbf{r}$. Пусть $\mathbf{r} = (1, 0, 1, 0)^T$.&lt;/p>
&lt;p>Вычисляется $\mathbf{u}$.
&lt;/p>
$$ \mathbf{u} = A^T \mathbf{r} = \begin{pmatrix} 2 &amp; 1 &amp; 14 &amp; 9 \\ 15 &amp; 8 &amp; 5 &amp; 10 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 2 \times 1 + 14 \times 1 \\ 15 \times 1 + 5 \times 1 \end{pmatrix} = \begin{pmatrix} 16 \\ 20 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 3 \end{pmatrix} \pmod{17} $$
&lt;p>Вычисляется $v$.
&lt;/p>
$$ \mathbf{b}^T \mathbf{r} = (16, 0, 11, 1) \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = 16 \times 1 + 11 \times 1 = 27 \equiv 10 \pmod{17} $$
&lt;p>
Прибавляется значение, соответствующее сообщению $M=1$: $\lfloor 17/2 \rfloor = 8$.
&lt;/p>
$$ v = \mathbf{b}^T \mathbf{r} + M \cdot 8 = 10 + 1 \times 8 = 18 \equiv 1 \pmod{17} $$
&lt;p>Алиса отправляет Бобу шифротекст $(\mathbf{u}, v) = \left( \begin{pmatrix} 16 \\ 3 \end{pmatrix}, 1 \right)$.&lt;/p>
&lt;p>&lt;strong>[3. Фаза дешифрования]&lt;/strong>
Боб, получивший шифротекст, расшифровывает его с помощью секретного ключа $\mathbf{s} = (3, 4)^T$.
Он вычисляет формулу процесса дешифрования: $D = v - \mathbf{s}^T \mathbf{u} \pmod{17}$.&lt;/p>
$$ \mathbf{s}^T \mathbf{u} = (3, 4) \begin{pmatrix} 16 \\ 3 \end{pmatrix} = 3 \times 16 + 4 \times 3 = 48 + 12 = 60 \equiv 9 \pmod{17} $$
$$ D = v - \mathbf{s}^T \mathbf{u} = 1 - 9 = -8 \pmod{17} $$
&lt;p>Здесь, в мире по модулю 17, $-8$ равно $9$ ($-8 + 17 = 9$).
Полученное значение $D = 9$ проверяется, к чему оно ближе: к $0$ или к $8$ ($\lfloor 17/2 \rfloor$).
Поскольку $9$ явно ближе к $8$, чем к $0$, Боб смог правильно восстановить $M = 1$!&lt;/p>
&lt;p>Почему получилось $9$? Давайте вспомним доказательство, приведенное ранее.
Часть ошибки равна $\mathbf{e}^T \mathbf{r} = (1, -1, 0, 2) (1, 0, 1, 0)^T = 1 \times 1 + 0 \times 1 = 1$.
Следовательно, результат вычислений равен $\mathbf{e}^T \mathbf{r} + M \cdot 8 = 1 + 8 = 9$, и мы убедились, что теоретическое значение было вычислено правильно.&lt;/p>
&lt;h1 id="7-эволюция-к-практическому-применению-ring-lwe-и-module-lwe">7. Эволюция к практическому применению: Ring-LWE и Module-LWE
&lt;/h1>&lt;p>Стандартная проблема LWE (Standard LWE), которую мы объясняли до сих пор, имеет очень сильное доказательство безопасности, но на практике у нее есть фатальные недостатки: &amp;ldquo;огромный размер ключа&amp;rdquo; и &amp;ldquo;высокая вычислительная стоимость&amp;rdquo;.&lt;/p>
&lt;p>В Standard LWE открытый ключ включает огромную матрицу $A \in \mathbb{Z}_q^{m \times n}$. Когда параметр $n$ достигает сотен или тысяч, размер этой матрицы достигает нескольких мегабайт, что слишком тяжело для передачи и приема каждый раз в интернет-протоколах связи (таких как TLS). Кроме того, умножение матрицы на вектор требует сложности вычислений $\mathcal{O}(n^2)$.&lt;/p>
&lt;p>Для решения этой проблемы были внедрены &amp;ldquo;Ring-LWE (RLWE)&amp;rdquo; и &amp;ldquo;Module-LWE (MLWE)&amp;rdquo;, которые включают алгебраическую структуру, называемую кольцами многочленов (Polynomial rings), в решетку.&lt;/p>
&lt;h2 id="71-интуиция-ring-lwe">7.1 Интуиция Ring-LWE
&lt;/h2>&lt;p>В Ring-LWE векторы и матрицы заменяются на элементы (многочлены) над кольцом многочленов $\mathcal{R}_q = \mathbb{Z}_q[X]/(X^n + 1)$. (Здесь $n$ выбирается как степень двойки).&lt;/p>
&lt;p>В то время как открытым ключом в Standard LWE была матрица $A$, Ring-LWE использует один многочлен $a(x)$. Секретный ключ $s(x)$ и ошибка $e(x)$ также становятся многочленами.
Уравнение выглядит следующим образом:
&lt;/p>
$$ b(x) = a(x) \cdot s(x) + e(x) \pmod q $$
&lt;p>Поскольку это умножение многочленов, за счет использования &amp;ldquo;теоретико-числового преобразования (Number Theoretic Transform: NTT)&amp;rdquo;, аналогичного быстрому преобразованию Фурье (FFT), вычислительная сложность может быть резко снижена до $\mathcal{O}(n \log n)$. Кроме того, поскольку размер открытого ключа уменьшается с матрицы до одного многочлена, размер данных сокращается до $\mathcal{O}(n)$. Это дает подавляющее преимущество в пропускной способности связи.&lt;/p>
&lt;p>С математической точки зрения Ring-LWE сводится к задаче на решетке с особой симметрией, называемой &amp;ldquo;идеальной решеткой&amp;rdquo; (Ideal Lattice), а не на общей решетке.&lt;/p>
&lt;h2 id="72-module-lwe-и-стандартизация-nist-kyber--ml-kem">7.2 Module-LWE и стандартизация NIST (Kyber / ML-KEM)
&lt;/h2>&lt;p>Хотя Ring-LWE эффективен, существовало некоторое опасение, что специальная алгебраическая структура идеальных решеток может стать отправной точкой для будущих атак. Поэтому &amp;ldquo;Module-LWE (MLWE)&amp;rdquo; был создан, чтобы взять &amp;ldquo;лучшее из обоих миров&amp;rdquo;: консервативную безопасность Standard LWE и эффективность Ring-LWE.&lt;/p>
&lt;p>В Module-LWE мы рассматриваем небольшие матрицы и векторы, элементами которых являются многочлены. Другими словами, мы работаем с модулями над кольцом.
В настоящее время &amp;ldquo;CRYSTALS-Kyber&amp;rdquo; (название стандарта: ML-KEM), выбранный NIST в качестве стандарта алгоритма инкапсуляции ключей (KEM) для PQC, построен именно на сложности этой проблемы Module-LWE.&lt;/p>
&lt;h1 id="8-почему-это-безопасно-против-квантовых-компьютеров">8. Почему это безопасно против квантовых компьютеров?
&lt;/h1>&lt;p>Наконец, мы коснемся сути вопроса: &amp;ldquo;Почему считается, что криптография на решетках не может быть взломана даже с помощью квантовых компьютеров?&amp;rdquo;&lt;/p>
&lt;p>Алгоритм Шора, с помощью которого квантовые компьютеры взламывают шифрование RSA и криптографию на эллиптических кривых, по сути, является алгоритмом для решения &amp;ldquo;проблемы скрытой подгруппы (Hidden Subgroup Problem: HSP)&amp;rdquo;. Математическая структура (конечные абелевы группы), лежащая в основе RSA и ECC, обладает периодичностью, и эта периодичность (скрытая подгруппа) может быть извлечена за один шаг путем использования специальной операции квантового алгоритма, называемой квантовым преобразованием Фурье (QFT).&lt;/p>
&lt;p>Однако задачи на решетках в корне отличаются. Решетки также имеют периодичность, но то, что ищется в SVP и CVP, — это геометрические нелинейные свойства, такие как &amp;ldquo;кратчайшее расстояние&amp;rdquo; и &amp;ldquo;удаление шума&amp;rdquo;. Даже если применить &amp;ldquo;квантовое преобразование Фурье на абелевой группе&amp;rdquo;, подобно алгоритму Шора, непосредственно к задачам на решетках, полезную информацию для их решения нельзя эффективно извлечь. На сегодняшний день не было найдено квантовых алгоритмов, способных решить SVP или LWE за полиномиальное время. Широко распространено мнение, что даже с мощью параллельных вычислений квантовых компьютеров единственным эффективным средством является поиск, близкий к полному перебору (ускорение в виде квадратного корня с помощью алгоритма Гровера).&lt;/p>
&lt;h1 id="9-заключение">9. Заключение
&lt;/h1>&lt;p>В этой статье мы подробно рассмотрели математическую интуицию криптографии на решетках, начиная с геометрического определения решеток, переходя к формулировке проблемы LWE и заканчивая построением криптографии с открытым ключом.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Решетка (Lattice)&lt;/strong> — это дискретное пространство, представленное линейными комбинациями базисных векторов с целыми коэффициентами, и в высоких измерениях становится трудно найти &amp;ldquo;хороший базис&amp;rdquo;, близкий к ортогональному (SVP).&lt;/li>
&lt;li>&lt;strong>Проблема LWE (Learning With Errors)&lt;/strong> — это проблема решения систем линейных уравнений с шумом. Поскольку она связана со сложностью задач на решетках в худшем случае, это обеспечивает сильное доказательство безопасности.&lt;/li>
&lt;li>Используя проблему LWE, шифрование и дешифрование (&lt;strong>Криптосистема Регева&lt;/strong>) реализуются с помощью хитроумного механизма намеренного добавления или удаления шума.&lt;/li>
&lt;li>В реальных протоколах для повышения эффективности связи и скорости вычислений используются &lt;strong>Ring-LWE&lt;/strong> и &lt;strong>Module-LWE&lt;/strong> с кольцами многочленов, которые служат основой для стандарта NIST &lt;strong>ML-KEM&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>В условиях надвигающегося беспрецедентного сдвига вычислительной парадигмы в виде квантовых компьютеров весьма романтично думать, что &amp;ldquo;криптография на решетках&amp;rdquo;, родившаяся из глубин классической линейной алгебры и теории чисел, сформирует основу будущей интернет-безопасности. Математика, лежащая в основе криптографии на решетках, не является чрезмерно сложной, и ее красивую структуру можно полностью понять, обладая базовыми знаниями в области линейной алгебры и теории вероятностей. Мы надеемся, что эта статья поможет вам понять криптографию на решетках, которая является ядром PQC.&lt;/p></description></item><item><title>Механизм доказательства с нулевым разглашением (ZKP) и его последние применения в Web3 и безопасности</title><link>http://kenji.blog/ru/p/zero-knowledge-proofs-zkp-web3-security/</link><pubDate>Fri, 11 Sep 2026 19:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/zero-knowledge-proofs-zkp-web3-security/</guid><description>&lt;img src="http://kenji.blog/p/zero-knowledge-proofs-zkp-web3-security/img/eyecatch.jpg" alt="Featured image of post Механизм доказательства с нулевым разглашением (ZKP) и его последние применения в Web3 и безопасности" />&lt;h2 id="введение">Введение
&lt;/h2>&lt;p>В современном цифровом обществе конфиденциальность данных и масштабируемость стали двумя наиболее важными проблемами. По мере роста риска утечек и несанкционированного использования личной информации, возникает острая необходимость в технологии, позволяющей «доказать, что вы обладаете определенной информацией, не раскрывая саму эту информацию другой стороне». Именно это и реализует &lt;strong>доказательство с нулевым разглашением (Zero-Knowledge Proof: ZKP)&lt;/strong>.&lt;/p>
&lt;p>Доказательство с нулевым разглашением — это криптографическая концепция, впервые предложенная в 1980-х годах Шафи Гольдвассер, Сильвио Микали и Чарльзом Ракоффом, однако долгое время она оставалась лишь предметом теоретических исследований. Но с появлением технологии блокчейн и Web3 ситуация кардинально изменилась. ZKP оказалось в центре внимания как «волшебная палочка», способная одновременно решить проблему масштабируемости (ограничения пропускной способности) и проблему конфиденциальности (все транзакции публичны), с которыми сталкиваются публичные блокчейны, такие как Ethereum.&lt;/p>
&lt;p>В этой статье мы подробно и глубоко с технической точки зрения рассмотрим основные концепции доказательства с нулевым разглашением, глубокие математические и криптографические механизмы &lt;strong>zk-SNARKs&lt;/strong> и &lt;strong>zk-STARKs&lt;/strong>, которые в настоящее время являются основными, а также последние примеры их применения в Web3 и сфере безопасности, такие как ZK-Rollups и децентрализованная идентификация (DID).&lt;/p>
&lt;hr>
&lt;h2 id="что-такое-доказательство-с-нулевым-разглашением-zkp">Что такое доказательство с нулевым разглашением (ZKP)?
&lt;/h2>&lt;p>Доказательство с нулевым разглашением (ZKP) означает протокол, при котором доказывающий (Prover) доказывает проверяющему (Verifier), что некое утверждение истинно, но при этом «не передает абсолютно никакой информации, кроме самого факта истинности этого утверждения».&lt;/p>
&lt;h3 id="3-требования-которым-должно-удовлетворять-zkp">3 требования, которым должно удовлетворять ZKP
&lt;/h3>&lt;p>Чтобы считаться ZKP, протокол должен строго выполнять следующие три свойства:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Полнота (Completeness)&lt;/strong>
Если утверждение истинно, и доказывающий с проверяющим добросовестно следуют протоколу, проверяющий должен принять доказательство (Accept) с подавляющей вероятностью.&lt;/li>
&lt;li>&lt;strong>Корректность (Soundness)&lt;/strong>
Если утверждение ложно, то никакой доказывающий-злоумышленник, какими бы вычислительными мощностями он ни обладал, не сможет обмануть проверяющего и заставить его принять доказательство (за исключением пренебрежимо малой вероятности).&lt;/li>
&lt;li>&lt;strong>Нулевое разглашение (Zero-Knowledge)&lt;/strong>
Если утверждение истинно, проверяющий не может получить из процесса доказательства никакой другой информации, кроме того факта, что «утверждение истинно». С точки зрения проверяющего, это доказывается математическим определением того, что процесс доказательства можно смоделировать (существует симулятор).&lt;/li>
&lt;/ol>
&lt;h3 id="интерактивные-и-неинтерактивные-доказательства">Интерактивные и неинтерактивные доказательства
&lt;/h3>&lt;p>ZKP делится на два типа: &lt;strong>интерактивное доказательство&lt;/strong>, при котором доказывающий и проверяющий обмениваются сообщениями несколько раз, и &lt;strong>неинтерактивное доказательство&lt;/strong>, при котором доказывающий отправляет данные доказательства только один раз.&lt;/p>
&lt;h4 id="интерактивное-доказательство-interactive-zkp">Интерактивное доказательство (Interactive ZKP)
&lt;/h4>&lt;p>Ранние ZKP разрабатывались как интерактивные протоколы. Известная притча о «пещере Али-Бабы» является примером такого подхода. Основной поток протокола выглядит следующим образом:&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Prover as "Prover (Доказывающий)"
participant Verifier as "Verifier (Проверяющий)"
Note over Prover, Verifier: "Основной процесс интерактивного доказательства"
Prover->>Verifier: "1. Отправить обязательство (Commitment)"
Verifier->>Prover: "2. Отправить случайный вызов (Challenge)"
Prover->>Verifier: "3. Вычислить и отправить ответ (Response)"
Note over Verifier: "Проверить ответ (Verification)"
Verifier-->>Prover: "4. Принять или отклонить (Accept / Reject)"
Note over Prover, Verifier: "※Для повышения достоверности это повторяется десятки раз"&lt;/div>
&lt;p>Этот метод эффективен, но требует, чтобы проверяющий находился онлайн, что делает его неудобным для применения в асинхронных распределенных системах, таких как блокчейн. В блокчейне любой желающий должен иметь возможность в любое время проверить прошлые доказательства.&lt;/p>
&lt;h4 id="эвристика-фиата-шамира-fiat-shamir-heuristic-и-переход-к-неинтерактивности">Эвристика Фиата-Шамира (Fiat-Shamir Heuristic) и переход к неинтерактивности
&lt;/h4>&lt;p>Революционным методом преобразования интерактивных доказательств в неинтерактивные (Non-Interactive Zero-Knowledge Proof: NIZK) является &lt;strong>преобразование (эвристика) Фиата-Шамира&lt;/strong>.&lt;/p>
&lt;p>Вместо «случайного вызова», отправляемого проверяющим, доказывающий самостоятельно генерирует «псевдослучайный вызов», используя собственное обязательство (commitment) и хеш-значение публичной информации. При условии, что криптографическая хеш-функция (например, SHA-256 или Keccak) работает как случайный оракул (Random Oracle), доказывающий не может заранее предсказать или манипулировать вызовом. Таким образом, доказательство завершается одной отправкой сообщения с сохранением безопасности, эквивалентной интерактивному доказательству.&lt;/p>
&lt;hr>
&lt;h2 id="технические-подробности-zk-snarks">Технические подробности zk-SNARKs
&lt;/h2>&lt;p>В настоящее время наиболее широко используемым видом ZKP является &lt;strong>zk-SNARKs&lt;/strong> (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge). Как следует из названия, это аргумент знания (Argument of Knowledge), который обладает нулевым разглашением (zk), имеет очень маленький размер доказательства и быстро проверяется (Succinct), а также является неинтерактивным (Non-Interactive).&lt;/p>
&lt;p>В основе zk-SNARKs лежит передовая алгебраическая геометрия и криптография. Они преобразуют выполнение программ и вычисления в проверку уравнений определенных многочленов.&lt;/p>
&lt;h3 id="1-арифметические-схемы-и-преобразование-в-r1cs-rank-1-constraint-system">1. Арифметические схемы и преобразование в R1CS (Rank-1 Constraint System)
&lt;/h3>&lt;p>Сначала любое вычисление, которое необходимо доказать (алгоритм или логика смарт-контракта), преобразуется в &lt;strong>арифметическую схему (Arithmetic Circuit)&lt;/strong>, состоящую из вентилей сложения и умножения.&lt;/p>
&lt;p>Затем эта арифметическая схема преобразуется в набор матричных уравнений, называемый &lt;strong>R1CS (Rank-1 Constraint System)&lt;/strong>. R1CS — это задача поиска матриц $A, B, C$, удовлетворяющих следующему ограничению для вектора переменных $x$:&lt;/p>
$$ (A \cdot x) \circ (B \cdot x) = C \cdot x $$
&lt;p>Здесь $\circ$ обозначает произведение Адамара (поэлементное умножение). Это ограничение гарантирует, что все логические вентили в схеме (особенно вентили умножения) вычислены правильно.&lt;/p>
&lt;h3 id="2-преобразование-в-qap-quadratic-arithmetic-program">2. Преобразование в QAP (Quadratic Arithmetic Program)
&lt;/h3>&lt;p>Поскольку матричных ограничений R1CS существует огромное количество, проверять их по отдельности крайне неэффективно. Поэтому с помощью интерполяции Лагранжа эти ограничения сжимаются в единое полиномиальное уравнение. Это называется &lt;strong>QAP (Quadratic Arithmetic Program)&lt;/strong>.&lt;/p>
&lt;p>За счет преобразования в QAP задача, которую необходимо доказать, сводится к вопросу: «Делится ли определенный многочлен $P(x)$ на другой известный многочлен $Z(x)$?»&lt;/p>
$$ P(x) = L(x) \cdot R(x) - O(x) $$
&lt;p>Здесь $L(x), R(x), O(x)$ — многочлены, составленные из соответствующих строк матриц $A, B, C$. Если доказывающий знает правильное решение (Witness), значение $P(x)$ будет равно 0 в каждом корне (точке оценки), поэтому $P(x)$ будет иметь целевой многочлен $Z(x)$ в качестве множителя. То есть существует некоторый многочлен $H(x)$, для которого выполняется:&lt;/p>
$$ P(x) = H(x) \cdot Z(x) $$
&lt;p>Проверяющий может мгновенно проверить правильность выполнения всего вычисления, просто проверив, выполняется ли это уравнение $P(s) = H(s) \cdot Z(s)$ в некоторой случайной секретной точке $s$. В этом заключается секрет «Succinct» (краткости).&lt;/p>
&lt;h3 id="3-эллиптическая-криптография-и-спаривание-bilinear-pairings">3. Эллиптическая криптография и спаривание (Bilinear Pairings)
&lt;/h3>&lt;p>Однако, если бы проверяющий знал секретную точку $s$, доказывающий мог бы подделать ложный многочлен для удовлетворения уравнения (нарушение корректности). Поэтому вычисления необходимо проводить, пока $s$ остается зашифрованным от всех (с использованием гомоморфного шифрования).&lt;/p>
&lt;p>Это достигается с помощью &lt;strong>эллиптического спаривания (Bilinear Pairings)&lt;/strong>.
Спаривание $e$ — это специальная функция, которая из двух зашифрованных значений позволяет вычислить значение, эквивалентное шифрованию их произведения.&lt;/p>
$$ e(g_1^a, g_2^b) = e(g_1, g_2)^{ab} $$
&lt;p>Доказывающий, не зная самой $s$, использует зашифрованные значения степеней $s$ (это называется CRS: Common Reference String) для вычисления зашифрованных значений многочленов $P(s)$ и $H(s)$. Проверяющий использует функцию спаривания, чтобы проверить выполнение отношения $P(s) = H(s) \cdot Z(s)$, оперируя при этом зашифрованными значениями.&lt;/p>
&lt;h3 id="4-доверенная-установка-trusted-setup">4. Доверенная установка (Trusted Setup)
&lt;/h3>&lt;p>Главной слабостью zk-SNARKs (особенно ранних версий, таких как Groth16) является необходимость процесса генерации секретной точки $s$, так называемой &lt;strong>доверенной установки (Trusted Setup)&lt;/strong>. Если создатель $s$ сохранит это значение вместо того, чтобы уничтожить его, он сможет сгенерировать любые ложные доказательства (проблема Toxic Waste).&lt;/p>
&lt;p>Для предотвращения этого проводится ритуал, называемый «Ceremony», с использованием многосторонних вычислений (Multi-Party Computation, MPC). В нем участвует множество участников, предоставляя случайные данные. Если хотя бы один участник честно уничтожит свое случайное значение, безопасность всей системы будет сохранена. Тем не менее, исследования по устранению этой зависимости продолжались долгие годы.&lt;/p>
&lt;hr>
&lt;h2 id="технические-подробности-zk-starks">Технические подробности zk-STARKs
&lt;/h2>&lt;p>В качестве ответа на зависимость от доверенной установки и риск взлома эллиптической криптографии квантовыми компьютерами появились &lt;strong>zk-STARKs&lt;/strong> (Zero-Knowledge Scalable Transparent Argument of Knowledge).&lt;/p>
&lt;p>Разработанные Эли Бен-Сассоном (Eli Ben-Sasson) и его коллегами, STARKs обладают характеристиками, отраженными в их названии: они «прозрачны» (Transparent) и вообще не требуют доверенной установки, а также «масштабируемы» (Scalable), поскольку размер доказательства и время проверки остаются эффективными даже при увеличении объема вычислений.&lt;/p>
&lt;h3 id="1-полиномиальные-обязательства-и-протокол-fri">1. Полиномиальные обязательства и протокол FRI
&lt;/h3>&lt;p>В отличие от zk-SNARKs, zk-STARKs не используют эллиптическую криптографию, а основывают свою безопасность &lt;strong>исключительно на хеш-функциях&lt;/strong>. Благодаря этому они обладают свойствами постквантовой криптографии (Post-Quantum Cryptography).&lt;/p>
&lt;p>Проверка вычислений выполняется после их преобразования в формат под названием AIR (Algebraic Intermediate Representation) с использованием свойств одномерных или многомерных полиномов. Сердцем STARKs является протокол &lt;strong>FRI (Fast Reed-Solomon Interactive Oracle Proof of Proximity)&lt;/strong>.&lt;/p>
&lt;p>Протокол FRI — это технология проверки того, «насколько близка определенная функция к полиному определенной степени» (Proximity). Доказывающий фиксирует значения полинома в качестве листьев дерева Меркла (Merkle Tree) (это называется полиномиальным обязательством).&lt;/p>
&lt;div class="mermaid">graph TD
Root["Корень Меркла (Обязательство)"] --> Node0["Узел 0"]
Root --> Node1["Узел 1"]
Node0 --> Leaf0["P(x_0)"]
Node0 --> Leaf1["P(x_1)"]
Node1 --> Leaf2["P(x_2)"]
Node1 --> Leaf3["P(x_3)"]&lt;/div>
&lt;p>Проверяющий запрашивает раскрытие нескольких случайных точек и использует доказательства Меркла (Merkle proof), чтобы подтвердить, что они включены в обязательство. Рекурсивно повторяя этот процесс, он с подавляющей вероятностью гарантирует, что степень исходного многочлена действительно низкая.&lt;/p>
&lt;h3 id="сравнение-zk-snarks-и-zk-starks">Сравнение zk-SNARKs и zk-STARKs
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Характеристика&lt;/th>
&lt;th style="text-align:left">zk-SNARKs&lt;/th>
&lt;th style="text-align:left">zk-STARKs&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Криптографические предположения&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Эллиптические кривые, спаривание&lt;/td>
&lt;td style="text-align:left">Криптографические хеш-функции, устойчивые к коллизиям&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Доверенная установка&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Требуется (в Plonk и др. — универсальная)&lt;/td>
&lt;td style="text-align:left">Не требуется (Transparent)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Квантовая устойчивость&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Нет&lt;/td>
&lt;td style="text-align:left">Есть&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Размер доказательства&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Очень маленький (~200 байт)&lt;/td>
&lt;td style="text-align:left">Довольно большой (десятки КБ)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Вычислительные затраты на создание доказательства&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Высокие&lt;/td>
&lt;td style="text-align:left">Относительно ниже, чем у SNARKs&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Затраты на проверку (Gas)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Очень низкие (постоянные)&lt;/td>
&lt;td style="text-align:left">Низкие (логарифмический рост)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>В последние годы появились SNARKs, такие как Plonk и Halo2, которым «не требуется доверенная установка или она нужна только один раз», поэтому граница между SNARKs и STARKs постепенно стирается, но принципиальная разница в математическом подходе остается важной.&lt;/p>
&lt;hr>
&lt;h2 id="новейшие-применения-доказательства-с-нулевым-разглашением-в-web3-и-безопасности">Новейшие применения доказательства с нулевым разглашением в Web3 и безопасности
&lt;/h2>&lt;p>Перейдя от теории к практике, ZKP в настоящее время производит революцию на переднем крае Web3 и кибербезопасности.&lt;/p>
&lt;h3 id="1-максимальное-масштабирование-ethereum-с-помощью-zk-rollups">1. Максимальное масштабирование Ethereum с помощью ZK-Rollups
&lt;/h3>&lt;p>Блокчейны уровня 1 (L1), такие как Ethereum, придают большое значение децентрализации и безопасности, но из-за этого имеют серьезные ограничения масштабируемости (трилемма). Окончательным решением уровня 2 (L2), устраняющим эту проблему, являются &lt;strong>ZK-Rollups&lt;/strong>.&lt;/p>
&lt;p>В ZK-Rollup тысячи транзакций выполняются и обрабатываются вне сети (L2), и генерируется «одно ZKP (Validity Proof)», подтверждающее правильность их выполнения. Смарт-контракту в блокчейне L1 остается только проверить это доказательство.&lt;/p>
&lt;div class="mermaid">flowchart LR
Users["Пользователи (Отправка Tx)"] --> Sequencer["Секвенсор (Сбор и выполнение Tx)"]
Sequencer --> Prover["Доказывающий (Генерация ZKP)"]
Sequencer --> L1Contract["L1 Смарт-контракт (Публикация данных Tx)"]
Prover --> L1Contract["Предоставление ZKP (Доказательства)"]
L1Contract --> Verify["Проверка и обновление состояния"]&lt;/div>
&lt;p>Главным преимуществом ZK-Rollups, в отличие от Optimistic Rollups (например, Arbitrum или Optimism), является отсутствие необходимости в периоде оспаривания (challenge period, обычно 7 дней) для доказательства мошенничества (Fraud Proof). Поскольку правильность криптографически гарантирована, как только доказательство проверено, вывод средств (Finality) на L1 завершается мгновенно. В настоящее время такие проекты, как zkSync, Starknet, Scroll и Polygon zkEVM, ведут ожесточенную конкуренцию в разработке, и реализация &lt;strong>zkEVM&lt;/strong>, совместимой с EVM (виртуальной машиной Ethereum), способствует быстрому росту экосистемы.&lt;/p>
&lt;h3 id="2-идентификация-с-сохранением-конфиденциальности-zkp-for-identity">2. Идентификация с сохранением конфиденциальности (ZKP for Identity)
&lt;/h3>&lt;p>ZKP также в корне меняет способы личной аутентификации в цифровом мире.
Например, на вопрос «Вам больше 18 лет?» в традиционных системах мы показываем водительские права или паспорт, передавая собеседнику ненужную личную информацию, такую как имя или адрес.&lt;/p>
&lt;p>Используя ZKP, на основе цифрового сертификата (Verifiable Credential), выданного государственным органом, можно &lt;strong>математически доказать только тот факт&lt;/strong>, что «согласно моей дате рождения, на текущую дату мне больше 18 лет». Проверяющему достаточно проверить подпись сертификата и ZKP, и он не сможет узнать вашу дату рождения или личность.&lt;/p>
&lt;p>Проекты «доказательства личности» (Proof of Personhood), такие как Worldcoin, также не хранят и не передают данные радужной оболочки глаза напрямую. Вместо этого они используют ZKP, чтобы доказать только то, что «вы — уникальный человек».&lt;/p>
&lt;h3 id="3-конфиденциальные-смарт-контракты-и-корпоративное-использование">3. Конфиденциальные смарт-контракты и корпоративное использование
&lt;/h3>&lt;p>Свойство публичных блокчейнов, при котором «все данные открыты», было серьезным препятствием для компаний при работе с конфиденциальными транзакциями или информацией о цепочках поставок в блокчейне.&lt;/p>
&lt;p>Используя технологии ZKP (например, сети, ориентированные на конфиденциальность, такие как Aleo или Aztec), можно записывать в публичный блокчейн только подтверждение правильности обновления состояния, оставляя входные и выходные значения транзакций и даже саму логику выполняемого смарт-контракта в зашифрованном виде. Это позволяет предотвратить фронтраннинг (MEV) в DeFi (децентрализованных финансах) и создать конфиденциальные сети консорциумов между компаниями, наслаждаясь при этом высокой безопасностью публичных блокчейнов.&lt;/p>
&lt;hr>
&lt;h2 id="будущие-проблемы-и-перспективы-zkp">Будущие проблемы и перспективы ZKP
&lt;/h2>&lt;p>ZKP, несомненно, является фундаментальной технологией следующего поколения, но остаются и некоторые проблемы.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Вычислительные затраты на генерацию доказательств и аппаратное ускорение&lt;/strong>
Генерация ZKP требует огромного количества полиномиальных операций, быстрого преобразования Фурье (FFT) и мультискалярного умножения (MSM). В настоящее время быстро продвигаются исследования в области разработки специализированного аппаратного обеспечения (FPGA или ASIC) для ускорения генерации доказательств, так называемый &lt;strong>майнинг ZKP&lt;/strong> (Prover Network).&lt;/li>
&lt;li>&lt;strong>Стандартизация и улучшение опыта разработчиков (DX)&lt;/strong>
Появилось множество специализированных языков для написания схем ZKP, таких как Circom, Cairo, Noir, Leo и другие. Зрелость компиляторов, которые автоматически генерируют схемы ZKP из существующих языков, таких как Rust или C++, и единые стандарты, объединяющие их, станут ключом к внедрению ZKP обычными инженерами-программистами.&lt;/li>
&lt;/ol>
&lt;h2 id="заключение">Заключение
&lt;/h2>&lt;p>Доказательство с нулевым разглашением (ZKP) превратилось из просто «технологии повышения анонимности криптовалют» в «универсальную технологию, переопределяющую доверие во всем интернете». Небольшие доказательства, вычисленные в глубинах формул и криптографии, станут щитом, который бесконечно расширяет масштабируемость блокчейна и надежно защищает нашу конфиденциальность.&lt;/p>
&lt;p>На пути к истинному массовому внедрению Web3 и созданию безопасного и приватного интернета следующего поколения, доказательство с нулевым разглашением будет продолжать играть роль важнейшего элемента. Мы должны внимательно следить за дальнейшей эволюцией технологий ZKP.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Источники и полезные ссылки&lt;/em>&lt;/p>
&lt;ul>
&lt;li>Groth, J. (2016). &amp;ldquo;On the Size of Pairing-based Non-interactive Arguments&amp;rdquo;&lt;/li>
&lt;li>Ben-Sasson, E., et al. (2018). &amp;ldquo;Scalable, transparent, and post-quantum secure computational integrity&amp;rdquo;&lt;/li>
&lt;li>Vitalik Buterin&amp;rsquo;s blog on zk-SNARKs and zk-STARKs&lt;/li>
&lt;/ul></description></item><item><title>Гипотеза Римана и распределение простых чисел: глубокая связь с современной криптографией</title><link>http://kenji.blog/ru/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/riemann-hypothesis-prime-distribution-cryptography/</guid><description>&lt;img src="http://kenji.blog/p/riemann-hypothesis-prime-distribution-cryptography/img/eyecatch.jpg" alt="Featured image of post Гипотеза Римана и распределение простых чисел: глубокая связь с современной криптографией" />&lt;h1 id="1-введение-тайна-простых-чисел-во-вселенной-и-гипотеза-римана">1. Введение: Тайна простых чисел во Вселенной и гипотеза Римана
&lt;/h1>&lt;p>«Простые числа» (Prime Numbers) — это натуральные числа, которые делятся только на 1 и на сами себя. В мире математики их часто называют «атомами». Эта последовательность, 2, 3, 5, 7, 11, 13&amp;hellip;, на первый взгляд кажется хаотичной и случайной. С тех пор как древнегреческий математик Евклид доказал, что «простых чисел бесконечно много», бесчисленное множество математиков пытались разгадать закономерности, скрывающиеся в этом ряду.&lt;/p>
&lt;p>Ближе всего к разгадке тайны простых чисел подошла &lt;strong>«Гипотеза Римана» (Riemann Hypothesis)&lt;/strong>, предложенная немецким математиком Бернхардом Риманом в 1859 году. Гипотеза Римана является одной из самых важных и нерешенных проблем в современной математике. Она также включена в список Задач тысячелетия Института Клэя, за решение которой назначена награда в 1 миллион долларов.&lt;/p>
&lt;p>На первый взгляд может показаться, что сложная проблема чистой математики, касающаяся распределения простых чисел, не имеет ничего общего с нашей повседневной жизнью. Однако безопасность инфраструктуры современного общества, в особенности интернета, опирается на &lt;strong>современные криптографические технологии, такие как шифрование RSA и криптография на эллиптических кривых (ECC)&lt;/strong>, которые глубоко зависят от свойств огромных простых чисел.&lt;/p>
&lt;p>В этой статье мы совершим математическое путешествие: от распределения простых чисел к теореме о распределении простых чисел, дзета-функции Римана и, наконец, к сути гипотезы Римана. Мы чрезвычайно подробно и глубоко рассмотрим, как всё это связано с современными криптографическими технологиями, и что произойдет с миром, если гипотеза Римана будет доказана.&lt;/p>
&lt;hr>
&lt;h1 id="2-теорема-о-распределении-простых-чисел-открытие-гаусса">2. Теорема о распределении простых чисел: Открытие Гаусса
&lt;/h1>&lt;p>Чтобы понять, как распределены простые числа, математики придумали &lt;strong>функцию распределения простых чисел (Prime-counting function)&lt;/strong> $\pi(x)$, которая показывает «сколько простых чисел существует до заданного числа $x$».&lt;/p>
&lt;p>Например:&lt;/p>
&lt;ul>
&lt;li>$\pi(10) = 4$ (2, 3, 5, 7)&lt;/li>
&lt;li>$\pi(100) = 25$&lt;/li>
&lt;li>$\pi(1000) = 168$&lt;/li>
&lt;/ul>
&lt;p>15-летний гениальный математик Карл Фридрих Гаусс, вычислив огромные таблицы простых чисел, обнаружил, что частота появления простых чисел убывает обратно пропорционально натуральному логарифму $\ln x$. Иными словами, он предположил, что вероятность найти простое число в окрестности числа $x$ составляет примерно $\frac{1}{\ln x}$.&lt;/p>
&lt;p>Выражение этой идеи с помощью интеграла называется &lt;strong>интегральным логарифмом (Logarithmic integral)&lt;/strong> $\text{Li}(x)$:&lt;/p>
$$ \text{Li}(x) = \int_{2}^{x} \frac{dt}{\ln t} $$
&lt;p>Предположение Гаусса было независимо доказано в 1896 году Жаком Адамаром и Шарлем Жан де ла Валле-Пуссеном, и утвердилось как &lt;strong>Теорема о распределении простых чисел (Prime Number Theorem, PNT)&lt;/strong>.&lt;/p>
$$ \lim_{x \to \infty} \frac{\pi(x)}{\text{Li}(x)} = 1 $$
&lt;p>Или же это можно приблизительно выразить следующим образом:&lt;/p>
$$ \pi(x) \sim \frac{x}{\ln x} $$
&lt;p>Эта теорема показала, что на макроскопическом уровне простые числа распределены очень плавно и предсказуемо. Однако на микроскопическом уровне между $\pi(x)$ и $\text{Li}(x)$ всегда существует «погрешность», то есть «флуктуация». Именно природа этой флуктуации является величайшей загадкой, которую пытается разгадать гипотеза Римана.&lt;/p>
&lt;hr>
&lt;h1 id="3-дзета-функция-римана-и-произведение-эйлера">3. Дзета-функция Римана и произведение Эйлера
&lt;/h1>&lt;p>Самым мощным оружием в анализе распределения простых чисел является &lt;strong>дзета-функция Римана (Riemann Zeta Function)&lt;/strong>. Изначально это был бесконечный ряд, определенный Леонардом Эйлером для вещественных чисел $s > 1$.&lt;/p>
$$ \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s} = 1 + \frac{1}{2^s} + \frac{1}{3^s} + \frac{1}{4^s} + \dots $$
&lt;p>Одно из величайших достижений Эйлера заключалось в том, что он доказал, что этот бесконечный ряд можно представить как бесконечное произведение по всем простым числам $p$. Это и есть &lt;strong>Тождество Эйлера (Euler Product Formula)&lt;/strong>.&lt;/p>
$$ \zeta(s) = \prod_{p \text{ prime}} \frac{1}{1 - p^{-s}} = \left( \frac{1}{1 - 2^{-s}} \right) \left( \frac{1}{1 - 3^{-s}} \right) \left( \frac{1}{1 - 5^{-s}} \right) \dots $$
&lt;p>Интуитивное понимание этого доказательства заключается в том, что если мы разложим каждый член правой части как геометрическую прогрессию и перемножим их, то, согласно основной теореме арифметики (каждое натуральное число единственным образом представляется в виде произведения простых чисел), мы полностью восстановим сумму обратных величин натуральных чисел в левой части.&lt;/p>
&lt;p>&lt;strong>Эта единственная формула стала мостом, соединившим математический анализ (бесконечные ряды и непрерывные функции) и теорию чисел (простые числа и дискретные числа).&lt;/strong> Изучение дзета-функции равносильно изучению распределения простых чисел.&lt;/p>
&lt;hr>
&lt;h1 id="4-аналитическое-продолжение-и-расширение-на-комплексную-плоскость">4. Аналитическое продолжение и расширение на комплексную плоскость
&lt;/h1>&lt;p>Гениальность Римана заключалась в том, что он расширил переменную $s$ в функции $\zeta(s)$, которую Эйлер рассматривал только для вещественных чисел, до &lt;strong>комплексного числа $s = \sigma + it$ (где $\sigma$ — действительная часть, а $t$ — мнимая часть)&lt;/strong>.&lt;/p>
&lt;p>Исходный бесконечный ряд сходится только при $\sigma > 1$, но Риман, используя метод «аналитического продолжения» (Analytic Continuation), расширил определение функции так, чтобы $\zeta(s)$ имела смысл на всей комплексной плоскости, за исключением полюса в точке $s = 1$.&lt;/p>
&lt;p>Далее он вывел красивое функциональное уравнение (Functional equation), которому удовлетворяет дзета-функция.&lt;/p>
$$ \zeta(s) = 2^s \pi^{s-1} \sin\left(\frac{\pi s}{2}\right) \Gamma(1-s) \zeta(1-s) $$
&lt;p>Здесь $\Gamma(x)$ — гамма-функция. Это уравнение позволяет узнать свойства функции в левой полуплоскости на основе её свойств в правой полуплоскости.&lt;/p>
&lt;h3 id="нули-zeros-of-the-zeta-function">Нули (Zeros of the Zeta Function)
&lt;/h3>&lt;p>Комплексные числа $s$, при которых значение дзета-функции становится равным 0, называются «нулями».
Из функционального уравнения следует, что когда $s$ — отрицательное четное число ($-2, -4, -6, \dots$), значение $\sin(\pi s / 2)$ равно 0, поэтому $\zeta(s) = 0$. Они называются &lt;strong>тривиальными нулями (Trivial zeros)&lt;/strong>.&lt;/p>
&lt;p>Однако для распределения простых чисел важны другие нули — те, что находятся в «критической полосе» (Critical strip) $0 \le \sigma \le 1$, так называемые &lt;strong>нетривиальные нули (Non-trivial zeros)&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="5-суть-гипотезы-римана-и-явная-формула">5. Суть гипотезы Римана и явная формула
&lt;/h1>&lt;p>Риман вычислил несколько таких нулей и выдвинул одно поразительное предположение. Это и есть &lt;strong>гипотеза Римана&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Гипотеза Римана (Riemann Hypothesis)&lt;/strong>
Все нетривиальные нули дзета-функции Римана $\zeta(s)$ лежат на прямой, действительная часть которой равна $1/2$ ($\text{Re}(s) = 1/2$).&lt;/p>
&lt;/blockquote>
&lt;p>Эта прямая с действительной частью 1/2 называется «критической прямой» (Critical line).&lt;/p>
&lt;div class="mermaid">graph TD
A["Дзета-функция Римана ζ(s)"] --> B["Расширение на комплексную плоскость аналитическим продолжением"]
B --> C["Тривиальные нули (s = -2, -4, -6 ...)"]
B --> D["Нетривиальные нули (0 &lt;= Re(s) &lt;= 1)"]
D --> E["Гипотеза Римана"]
E --> F["Все нетривиальные нули лежат на Re(s) = 1/2"]
F --> G["К доказательству предела погрешности распределения простых чисел"]&lt;/div>
&lt;p>Почему гипотеза Римана так важна? Потому что нули дзета-функции &lt;strong>полностью&lt;/strong> определяют распределение простых чисел.&lt;/p>
&lt;p>Риман и более поздний математик фон Мангольдт вывели «явную формулу» (Explicit formula), которая точно описывает распределение простых чисел. Используя функцию Чебышёва $\psi(x)$, это можно выразить следующим образом:&lt;/p>
$$ \psi(x) = x - \sum_{\rho} \frac{x^\rho}{\rho} - \ln(2\pi) - \frac{1}{2}\ln(1 - x^{-2}) $$
&lt;p>Здесь сумма берется по всем нетривиальным нулям $\rho$ дзета-функции.
Главным членом является $x$ (что соответствует теореме о распределении простых чисел), а путем прибавления и вычитания волнообразных членов, зависящих от нулей $\rho$, восстанавливается точное ступенчатое распределение простых чисел. Можно сказать, что нетривиальные нули представляют собой «частоты (волны)» распределения простых чисел.&lt;/p>
&lt;p>Если гипотеза Римана верна, и действительная часть всех нетривиальных нулей $\rho$ в точности равна $1/2$, то член погрешности в теореме о распределении простых чисел будет укладываться в минимально возможный теоретический диапазон.&lt;/p>
$$ |\pi(x) - \text{Li}(x)| \le \frac{1}{8\pi} \sqrt{x} \ln x \quad \text{for} \quad x \ge 2657 $$
&lt;p>Другими словами, &lt;strong>если гипотеза Римана истинна, это докажет, что простые числа распределены настолько «правильно и красиво», насколько мы только можем себе представить&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="6-неразрывная-связь-современных-криптографических-технологий-и-простых-чисел">6. Неразрывная связь современных криптографических технологий и простых чисел
&lt;/h1>&lt;p>До этого момента мы находились в мире глубокой чистой математики, но именно эти свойства простых чисел фундаментально поддерживают современное цифровое общество. Ярким примером является криптография с открытым ключом, такая как &lt;strong>шифрование RSA&lt;/strong>.&lt;/p>
&lt;p>Безопасность любых коммуникаций, будь то оплата кредитной картой в интернете, передача паролей или электронные подписи в блокчейне, зависит от «простых чисел».&lt;/p>
&lt;h3 id="как-работает-шифрование-rsa">Как работает шифрование RSA
&lt;/h3>&lt;p>Безопасность RSA основана на математическом факте (задаче факторизации), что «очень сложно разложить большое составное число на простые множители».&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Генерация ключей&lt;/strong>:
Случайным образом выбираются два огромных простых числа $p$ и $q$ (например, по 2048 бит каждое).
Они перемножаются для получения $N = p \times q$. Это $N$ становится частью открытого ключа.
Используя функцию Эйлера $\phi(N) = (p-1)(q-1)$, генерируется закрытый ключ $d$.&lt;/p>
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Шифрование и расшифрование&lt;/strong>:
Открытый текст $M$ преобразуется в зашифрованный текст $C$ с использованием открытого ключа $e, N$.
&lt;/p>
$$ C \equiv M^e \pmod{N} $$
&lt;p>
Расшифровать текст может только тот, кто обладает закрытым ключом $d$.
&lt;/p>
$$ M \equiv C^d \pmod{N} $$
&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["Открытый текст (Plaintext)"] --> B["Шифрование открытым ключом (e, N)"]
B --> C["Зашифрованный текст (Ciphertext)"]
C --> D["Расшифрование закрытым ключом (d)"]
D --> E["Исходный открытый текст"]
F["Злоумышленник (Attacker)"] -- "Попытка факторизации N" --> C
F -.-> G["Без знания p и q вычислить d невозможно"]&lt;/div>
&lt;p>Чтобы взломать шифр RSA, нужно найти (факторизовать) исходные простые числа $p$ и $q$ из гигантского числа $N$. Считается, что даже с использованием самых передовых на сегодня алгоритмов (таких как общий метод решета числового поля, GNFS) и суперкомпьютеров, разложение числа из сотен цифр займет время, намного превышающее возраст Вселенной.&lt;/p>
&lt;hr>
&lt;h1 id="7-влияние-гипотезы-римана-на-криптографические-технологии">7. Влияние гипотезы Римана на криптографические технологии
&lt;/h1>&lt;p>Так как же пересекаются «гипотеза Римана», находящаяся на вершине чистой математики, и «криптография»?&lt;/p>
&lt;h3 id="71-алгоритмы-генерации-простых-чисел-проверка-на-простоту-и-обобщенная-гипотеза-римана-grh">7.1. Алгоритмы генерации простых чисел (проверка на простоту) и Обобщенная гипотеза Римана (GRH)
&lt;/h3>&lt;p>Для работы шифрования RSA необходимо сначала сгенерировать огромные простые числа $p$ и $q$. Однако надежно и быстро определить, «является ли число простым», не так-то просто.&lt;/p>
&lt;p>В настоящее время на практике применяется вероятностный алгоритм — &lt;strong>тест простоты Миллера-Рабина (Miller-Rabin primality test)&lt;/strong>. Этот алгоритм быстр, но существует крайне низкая вероятность того, что составное число будет ошибочно признано простым (так называемые «псевдопростые числа»).&lt;/p>
&lt;p>Однако, если предположить, что &lt;strong>«Обобщенная гипотеза Римана» (Generalized Riemann Hypothesis, GRH)&lt;/strong>, которая распространяет гипотезу Римана на L-функции Дирихле, верна, то ситуация кардинально меняется.
Если GRH истинна, математически гарантируется верхняя граница количества проверок в тесте Миллера-Рабина, и вероятностный алгоритм &lt;strong>превращается в «детерминированный алгоритм полиномиального времени»&lt;/strong> (этот важный факт был известен еще до открытия теста простоты AKS).&lt;/p>
&lt;p>Таким образом, гипотеза Римана (и ее обобщение) играет роль непосредственного гаранта того, «можно ли с абсолютной уверенностью и высокой скоростью генерировать гигантские простые числа», что является фундаментом для создания шифров.&lt;/p>
&lt;h3 id="72-связь-с-алгоритмами-факторизации">7.2. Связь с алгоритмами факторизации
&lt;/h3>&lt;p>Знания о распределении простых чисел также необходимы при оценке вычислительной сложности алгоритмов взлома шифров (таких как общий метод решета числового поля). Многие алгоритмы факторизации опираются на распределение «гладких чисел» (Smooth numbers — чисел, имеющих только небольшие простые множители).&lt;/p>
&lt;p>Чтобы точно оценить частоту появления гладких чисел, необходимо глубокое понимание распределения простых чисел. Здесь также используются методы аналитической теории чисел, напрямую связанные с дзета-функцией и гипотезой Римана. Если гипотеза Римана будет доказана, и погрешность распределения простых чисел будет полностью определена, станет возможным более точно установить пределы эффективности алгоритмов факторизации.&lt;/p>
&lt;hr>
&lt;h1 id="8-если-гипотеза-римана-будет-доказана-будут-ли-взломаны-шифры">8. Если гипотеза Римана будет доказана, будут ли взломаны шифры?
&lt;/h1>&lt;p>В качестве городской легенды иногда можно услышать, что «если гипотеза Римана будет решена, шифрование RSA рухнет в одно мгновение», но &lt;strong>математически это неверно&lt;/strong>.&lt;/p>
&lt;p>Само доказательство гипотезы Римана не приведет к мгновенному появлению магического алгоритма, который радикально ускорит факторизацию. Гипотеза Римана — это теорема о макроскопической «закономерности распределения» простых чисел, она не сообщает напрямую о том, на какие именно простые числа делится конкретное число $N$ (локальные свойства).&lt;/p>
&lt;p>Однако влияние не будет нулевым.
В процессе доказательства гипотезы Римана с огромной вероятностью &lt;strong>могут быть открыты «новые математические инструменты» и «неизвестные аналитические методы»&lt;/strong>. Как показывает история, доказательство Великой теоремы Ферма или гипотезы Пуанкаре породило новые теории, которые привели к колоссальному скачку в математике в целом.&lt;/p>
&lt;p>Если будут разработаны неизвестные ранее методы алгебраической геометрии или некоммутативной геометрии, позволяющие полностью манипулировать свойствами нулей дзета-функции Римана, нельзя исключать, что это в конечном итоге приведет к открытию революционного алгоритма факторизации (например, классического алгоритма, который снизит время вычислений до полиномиального). В этом смысле криптографы никогда не смогут отвести взгляд от того, что происходит вокруг гипотезы Римана.&lt;/p>
&lt;h3 id="квантовые-компьютеры-и-алгоритм-шора">Квантовые компьютеры и алгоритм Шора
&lt;/h3>&lt;p>Куда более прямой и реальной угрозой для криптографических технологий является не доказательство гипотезы Римана, а &lt;strong>квантовые компьютеры&lt;/strong>. Опубликованный Питером Шором (Peter Shor) в 1994 году «алгоритм Шора» доказал, что при наличии достаточно мощного квантового компьютера факторизация может быть решена за полиномиальное время. Это фундаментально разрушит шифрование RSA и криптографию на эллиптических кривых.&lt;/p>
&lt;p>В настоящее время по всему миру идет переход на &lt;strong>постквантовую криптографию (Post-Quantum Cryptography, PQC)&lt;/strong> (например, криптографию на решетках), которую не смогут взломать даже квантовые компьютеры. Криптографические технологии, основанные на простых числах, возможно, подходят к концу своего золотого века, но математическая ценность самих простых чисел не будет утрачена никогда.&lt;/p>
&lt;hr>
&lt;h1 id="9-заключение-перекресток-математической-абстракции-и-реального-мира">9. Заключение: Перекресток математической абстракции и реального мира
&lt;/h1>&lt;div class="mermaid">graph TD
A["Исследования в чистой математике"] --> B["Разгадка гипотезы Римана"]
B --> C["Полное понимание распределения простых чисел"]
C --> D["Стремительное развитие теории чисел и алгебраической геометрии"]
D -.-> E["Вероятность появления новых алгоритмов факторизации"]
E -.-> F["Обновление оценки безопасности криптографических технологий"]
A --> G["Прикладная математика и информатика"]
G --> H["Оптимизация проверки на простоту и генерации шифров"]
H --> F&lt;/div>
&lt;p>Неустанные поиски простых чисел, ведущиеся со времен Древней Греции, благодаря гению Римана превратились в прекрасную симфонию на комплексной плоскости (нули дзета-функции). И поразительно, что этот кристалл чистейшей математики спустя столетия применяется как самый мощный щит, гарантирующий безопасность интернет-общества.&lt;/p>
&lt;p>Гипотеза Римана символизирует одновременно «абстрактную красоту» математики и её «удивительную применимость в физическом мире и реальном обществе».&lt;/p>
&lt;p>Когда эта гигантская математическая гора, вершины которой еще никто не достиг, будет однажды покорена, мы не только полностью поймем истину Вселенной, скрытую в простых числах, но и обретем новый взгляд на фундамент нашего информационного общества. Изучение криптографии — это, по сути, путешествие по страницам истории человеческой мудрости.&lt;/p></description></item><item><title>История криптографии: от шифра Цезаря до постквантовой криптографии (PQC)</title><link>http://kenji.blog/ru/p/history-of-cryptography-caesar-to-pqc/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/history-of-cryptography-caesar-to-pqc/</guid><description>&lt;img src="http://kenji.blog/p/history-of-cryptography-caesar-to-pqc/img/eyecatch.jpg" alt="Featured image of post История криптографии: от шифра Цезаря до постквантовой криптографии (PQC)" />&lt;h1 id="1-введение-что-такое-криптография">1. Введение: Что такое криптография?
&lt;/h1>&lt;p>Криптография (Cryptography) — это технология сохранения конфиденциальности информации, которая развивалась вместе с историей человечества. От передачи секретных приказов на древних войнах до защиты данных кредитных карт в современном интернете, цель криптографии остается неизменной: &amp;ldquo;Сделать так, чтобы только предполагаемый получатель мог понять информацию, и чтобы третья сторона не смогла ее расшифровать&amp;rdquo;.&lt;/p>
&lt;p>В современной информационной безопасности криптография играет важную роль, не ограничиваясь простой &amp;ldquo;секретностью информации (Конфиденциальность: Confidentiality)&amp;rdquo;, но и обеспечивая &amp;ldquo;целостность (Integrity)&amp;rdquo;, &amp;ldquo;аутентификацию (Authentication)&amp;rdquo; и &amp;ldquo;неотрекаемость (Non-repudiation)&amp;rdquo; данных.&lt;/p>
&lt;p>В этой статье мы подробно рассмотрим историю развития криптографии с технологической и математической точек зрения, начиная с простых древних шифров замены, переходя к механическим шифрам, современной криптографии с симметричным и открытым ключом, и заканчивая эрой &amp;ldquo;постквантовой криптографии (PQC)&amp;rdquo;, которая наступит с практическим применением квантовых компьютеров.&lt;/p>
&lt;hr>
&lt;h1 id="2-эпоха-классической-криптографии-замена-и-перестановка-символов">2. Эпоха классической криптографии: Замена и перестановка символов
&lt;/h1>&lt;p>Происхождение криптографии восходит к временам до нашей эры. Ранние шифры в основном состояли из двух подходов: &amp;ldquo;перестановка&amp;rdquo; и &amp;ldquo;замена&amp;rdquo;.&lt;/p>
&lt;h2 id="шифр-скитала-шифр-перестановки">Шифр Скитала (Шифр перестановки)
&lt;/h2>&lt;p>&amp;ldquo;Скитала&amp;rdquo; (Scytale), использовавшаяся в древней Греции в Спарте в 5 веке до н.э., является одним из старейших криптографических устройств. Длинная полоска пергамента наматывалась на деревянную палочку определенной толщины, и на ней писалось сообщение поперек. Когда пергамент разматывался, буквы оказывались в бессмысленном порядке, но получатель с палочкой такой же толщины мог снова намотать пергамент и прочитать исходное сообщение.&lt;/p>
&lt;h2 id="шифр-цезаря-шифр-простой-замены">Шифр Цезаря (Шифр простой замены)
&lt;/h2>&lt;p>Шифр Цезаря — это шифр, который, как считается, использовал Юлий Цезарь, герой Древнего Рима в 1 веке до н.э. Это шифр моноалфавитной подстановки (Monoalphabetic substitution), который сдвигает алфавит на фиксированное количество позиций (обычно на 3 буквы).&lt;/p>
&lt;p>Математически, если рассматривать буквы как числа от $0$ до $25$, а величину сдвига как $K$, преобразование исходного текста $P$ в зашифрованный текст $C$ выражается следующим уравнением по модулю:&lt;/p>
$$C \equiv P + K \pmod{26}$$
&lt;p>Расшифровка выполняется обратной операцией:&lt;/p>
$$P \equiv C - K \pmod{26}$$
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример простой реализации шифра Цезаря на Python&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">caesar_cipher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">shift&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;encrypt&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;decrypt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shift&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">shift&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">char&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isalpha&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;A&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">char&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isupper&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисление сдвига&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nb">chr&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">base&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">shift&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">26&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">char&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример выполнения&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">plaintext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HELLO WORLD&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">caesar_cipher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plaintext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;encrypt&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Зашифрованный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># KHOOR ZRUOG&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="частотный-анализ-и-шифр-виженера">Частотный анализ и шифр Виженера
&lt;/h2>&lt;p>Шифры моноалфавитной подстановки стали легко взламываться с помощью &amp;ldquo;частотного анализа&amp;rdquo; (Frequency Analysis), изобретенного арабским ученым Аль-Кинди в 9 веке. Он использует статистические особенности языка, например, то, что в английском языке буквы &amp;ldquo;E&amp;rdquo; и &amp;ldquo;T&amp;rdquo; встречаются чаще всего.&lt;/p>
&lt;p>Чтобы противостоять этому, в 16 веке был изобретен &amp;ldquo;шифр Виженера&amp;rdquo; (Vigenère cipher). Это полиалфавитный шифр замены (Polyalphabetic substitution), который циклически переключает несколько сдвигов (ключей), и около 300 лет его называли &amp;ldquo;неразрешимым шифром&amp;rdquo; (Le Chiffre Indéchiffrable).&lt;/p>
&lt;p>Математически $i$-я буква исходного текста $P_i$ и $i$-я буква повторяющегося ключа $K_i$ шифруются следующим образом:&lt;/p>
$$C_i \equiv P_i + K_i \pmod{26}$$
&lt;p>Однако в 19 веке Чарльз Бэббидж и Фридрих Касиски открыли &amp;ldquo;метод Касиски&amp;rdquo; (Kasiski examination), который позволял определить длину ключа по повторяющимся паттернам в зашифрованном тексте, и шифр был взломан.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Классификация классической криптографии"
A["Классическая криптография"] --> B["Шифр перестановки"]
A --> C["Шифр замены"]
B --> D["Шифр Скитала"]
C --> E["Моноалфавитная замена"]
C --> F["Полиалфавитная замена"]
E --> G["Шифр Цезаря"]
F --> H["Шифр Виженера"]
end&lt;/div>
&lt;hr>
&lt;h1 id="3-механическая-криптография-и-мировые-войны-энигма-и-ее-расшифровка">3. Механическая криптография и мировые войны: Энигма и ее расшифровка
&lt;/h1>&lt;p>В начале 20 века средства связи перешли от писем к телеграфу и радио, что потребовало увеличения скорости и сложности шифрования. В это время появилась &amp;ldquo;механическая криптография&amp;rdquo;, использующая комбинацию роторов (вращающихся дисков).&lt;/p>
&lt;h2 id="угроза-энигмы-enigma">Угроза Энигмы (Enigma)
&lt;/h2>&lt;p>&amp;ldquo;Энигма&amp;rdquo;, использовавшаяся нацистской Германией во время Второй мировой войны, является самой известной шифровальной машиной в истории криптографии. Энигма состояла из нескольких роторов (обычно 3-4), коммутационной панели (Steckerbrett), меняющей проводку букв, и рефлектора (отражающего ротора).&lt;/p>
&lt;p>Каждый раз при вводе буквы с клавиатуры ротор вращался, поэтому даже при последовательном вводе одной и той же буквы выводились разные зашифрованные символы (высшая форма полиалфавитного шифра). Пространство ключей (комбинаций настроек) составляло около $1.58 \times 10^{19}$ вариантов (около 15,8 квинтиллионов), и считалось, что взломать ее полным перебором при технологиях того времени было невозможно.&lt;/p>
&lt;h2 id="алан-тьюринг-и-бомба-bombe">Алан Тьюринг и &amp;ldquo;Бомба&amp;rdquo; (Bombe)
&lt;/h2>&lt;p>Вызов этой неприступной Энигме бросила команда криптоаналитиков из Блетчли-парка в Великобритании, опираясь на ранние результаты польского математика Мариана Реевского и его коллег.&lt;/p>
&lt;p>В частности, Алан Тьюринг (Alan Turing) использовал предположения об исходном тексте, соответствующем части зашифрованного текста (криб: Crib), и разработал электромеханическую машину для расшифровки, названную &amp;ldquo;Бомба&amp;rdquo; (Bombe). &amp;ldquo;Бомба&amp;rdquo; быстро обнаруживала логические противоречия и последовательно исключала невозможные настройки роторов, что позволило успешно расшифровать Энигму. Считается, что это великое достижение приблизило победу союзников на несколько лет.&lt;/p>
&lt;hr>
&lt;h1 id="4-начало-современной-криптографии-криптография-с-симметричным-ключом-des-и-aes">4. Начало современной криптографии: Криптография с симметричным ключом (DES и AES)
&lt;/h1>&lt;p>После войны с появлением компьютеров шифрование претерпело радикальную смену парадигмы: от манипуляций с &amp;ldquo;символами&amp;rdquo; к операциям с &amp;ldquo;битами (0 и 1)&amp;rdquo;.&lt;/p>
&lt;h2 id="клод-шеннон-и-теория-информации">Клод Шеннон и теория информации
&lt;/h2>&lt;p>В 1949 году Клод Шеннон опубликовал работу &amp;ldquo;Теория связи в секретных системах&amp;rdquo;, заложив математические основы современной криптографии. Он предложил &amp;ldquo;запутывание&amp;rdquo; (Confusion) и &amp;ldquo;рассеивание&amp;rdquo; (Diffusion) в качестве принципов безопасного проектирования шифров.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Запутывание (Confusion)&lt;/strong>: Сделать взаимосвязь между ключом и зашифрованным текстом как можно более сложной. (Реализуется через замену / S-блоки)&lt;/li>
&lt;li>&lt;strong>Рассеивание (Diffusion)&lt;/strong>: Сделать так, чтобы изменение одного бита исходного текста влияло на множество битов зашифрованного текста. (Реализуется через перестановку / перемешивание)&lt;/li>
&lt;/ul>
&lt;h2 id="des-data-encryption-standard">DES (Data Encryption Standard)
&lt;/h2>&lt;p>В 1977 году Национальный институт стандартов и технологий США (NIST, тогда NBS) принял &amp;ldquo;DES&amp;rdquo;, основанный на дизайне IBM, в качестве стандартного шифра.
DES использует архитектуру, называемую &amp;ldquo;Сеть Фейстеля&amp;rdquo; (Feistel Network), имеет размер блока 64 бита и длину ключа 56 бит. Это имело преимущество в реализации, так как алгоритмы шифрования и дешифрования имели почти одинаковую структуру.&lt;/p>
&lt;p>Однако по мере роста вычислительных мощностей компьютеров стало ясно, что длины ключа в 56 бит (около $7.2 \times 10^{16}$ вариантов) недостаточно. В 1998 году Electronic Frontier Foundation (EFF) разработал специальную машину &amp;ldquo;Deep Crack&amp;rdquo;, которая смогла взломать DES за несколько дней.&lt;/p>
&lt;h2 id="aes-advanced-encryption-standard">AES (Advanced Encryption Standard)
&lt;/h2>&lt;p>В качестве нового стандарта, пришедшего на смену DES, в 2001 году был принят &amp;ldquo;AES&amp;rdquo;. Был выбран алгоритм &amp;ldquo;Rijndael&amp;rdquo;, разработанный бельгийскими криптографами по итогам открытого конкурса.&lt;/p>
&lt;p>AES использует не Сеть Фейстеля, а &amp;ldquo;SPN-структуру&amp;rdquo; (Substitution-Permutation Network) и применяет математические операции в поле Галуа (конечном поле) $GF(2^8)$. Длину ключа можно выбрать из 128, 192 и 256 бит, и сегодня он широко используется по всему миру как стандартный симметричный алгоритм шифрования.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "1 раунд обработки AES (SPN-структура)"
A["Состояние входа (128-bit)"] --> B("SubBytes (Байтовая замена / S-Box)")
B --> C("ShiftRows (Сдвиг строк)")
C --> D("MixColumns (Смешивание столбцов / Умножение в GF(2^8))")
D --> E("AddRoundKey (XOR с раундовым ключом)")
E --> F["Переход к следующему раунду"]
end&lt;/div>
&lt;hr>
&lt;h1 id="5-революция-криптографии-с-открытым-ключом-от-диффи-хеллмана-к-rsa">5. Революция криптографии с открытым ключом: От Диффи-Хеллмана к RSA
&lt;/h1>&lt;p>Криптография с симметричным ключом имела существенный недостаток. Это &amp;ldquo;проблема распределения ключей&amp;rdquo; (Key Distribution Problem). Проблема заключалась в том, как безопасно разделить &amp;ldquo;общий ключ&amp;rdquo; с удаленным абонентом до начала зашифрованного общения. Эта проблема была решена в 1970-х годах с появлением &amp;ldquo;криптографии с открытым ключом&amp;rdquo;.&lt;/p>
&lt;h2 id="обмен-ключами-диффи-хеллмана">Обмен ключами Диффи-Хеллмана
&lt;/h2>&lt;p>В 1976 году Уитфилд Диффи и Мартин Хеллман опубликовали прорывную статью &amp;ldquo;Новые направления в криптографии&amp;rdquo;. Они предложили метод безопасного совместного использования ключа даже по прослушиваемому каналу связи, используя математическую сложность &amp;ldquo;задачи дискретного логарифмирования&amp;rdquo; (Discrete Logarithm Problem).&lt;/p>
&lt;ol>
&lt;li>Публикуются большое простое число $p$ и генератор $g$.&lt;/li>
&lt;li>Алиса выбирает секретное значение $a$ и отправляет Бобу $A = g^a \pmod{p}$.&lt;/li>
&lt;li>Боб выбирает секретное значение $b$ и отправляет Алисе $B = g^b \pmod{p}$.&lt;/li>
&lt;li>Алиса вычисляет $K = B^a \pmod{p}$, а Боб вычисляет $K = A^b \pmod{p}$.&lt;/li>
&lt;li>По закону степеней $K = (g^b)^a = (g^a)^b = g^{ab} \pmod{p}$, и они успешно разделяют один и тот же ключ $K$.&lt;/li>
&lt;/ol>
&lt;h2 id="алгоритм-rsa">Алгоритм RSA
&lt;/h2>&lt;p>В следующем 1977 году Рон Ривест, Ади Шамир и Леонард Адлеман изобрели &amp;ldquo;алгоритм RSA&amp;rdquo;. Он основан на том свойстве, что &amp;ldquo;разложение огромного составного числа на простые множители является сложным&amp;rdquo;.&lt;/p>
&lt;p>&lt;strong>Математический механизм RSA:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Выбираются два огромных простых числа $p$ и $q$, и вычисляется $n = p \times q$.&lt;/li>
&lt;li>Вычисляется функция Эйлера $\phi(n) = (p-1)(q-1)$.&lt;/li>
&lt;li>Выбирается целое число $e$ (открытый ключ), взаимно простое с $\phi(n)$.&lt;/li>
&lt;li>Вычисляется целое число $d$ (закрытый ключ), такое что $e \times d \equiv 1 \pmod{\phi(n)}$.&lt;/li>
&lt;/ol>
&lt;p>Шифрование: Для исходного текста $M$ вычисляется $C \equiv M^e \pmod{n}$
Дешифрование: Для зашифрованного текста $C$ вычисляется $M \equiv C^d \pmod{n}$&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Концептуальный код RSA на Python (не для практического использования)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">ext_euclid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисление модульного обратного с помощью расширенного алгоритма Евклида&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ext_euclid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">g&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">rsa_example&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Пример с небольшими простыми числами&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">61&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">53&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">17&lt;/span> &lt;span class="c1"># Значение, взаимно простое с phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ext_euclid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Открытый ключ: (e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, n=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Закрытый ключ: (d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, n=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Шифрование и расшифровка сообщения&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">65&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decrypted&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Исходный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> -&amp;gt; Зашифрованный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> -&amp;gt; После расшифровки: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rsa_example&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h1 id="6-расцвет-криптографии-на-эллиптических-кривых-ecc">6. Расцвет криптографии на эллиптических кривых (ECC)
&lt;/h1>&lt;p>RSA — мощный алгоритм, но по мере роста производительности компьютеров возникла проблема увеличения вычислительных затрат из-за необходимости увеличивать длину ключа для поддержания безопасности (в настоящее время 2048 или 3072 бита).&lt;/p>
&lt;p>Поэтому в 1985 году была предложена &amp;ldquo;криптография на эллиптических кривых&amp;rdquo; (Elliptic Curve Cryptography: ECC). Она использует сложение точек на эллиптической кривой (обычно вида $y^2 = x^3 + ax + b$) над конечным полем.&lt;/p>
&lt;p>Известно, что задачу дискретного логарифмирования на эллиптической кривой (ECDLP) решить еще сложнее, чем задачу факторизации, и &lt;strong>ECC может обеспечить уровень безопасности, эквивалентный 3072 битам RSA, при длине ключа всего 256 бит&lt;/strong>. Это позволило использовать быструю и безопасную зашифрованную связь (например, ECDSA и ECDH) даже в средах с ограниченными вычислительными ресурсами, таких как смартфоны и устройства IoT.&lt;/p>
&lt;hr>
&lt;h1 id="7-угроза-квантовых-компьютеров-и-постквантовая-криптография-pqc">7. Угроза квантовых компьютеров и постквантовая криптография (PQC)
&lt;/h1>&lt;p>Казалось, что криптографические технологии непоколебимы, но алгоритм Шора (Shor&amp;rsquo;s algorithm), опубликованный Питером Шором в 1994 году, вызвал огромный шок.&lt;/p>
&lt;p>Квантовые компьютеры выполняют вычисления, используя такие свойства квантовой механики, как &amp;ldquo;суперпозиция&amp;rdquo; и &amp;ldquo;квантовая запутанность&amp;rdquo;. Было математически доказано, что если выполнить алгоритм Шора на квантовом компьютере достаточной мощности, то задача факторизации и задача дискретного логарифмирования могут быть решены за &amp;ldquo;полиномиальное время&amp;rdquo;. Это означает, что в день появления практичного квантового компьютера (Q-Day) вся используемая сегодня криптография с открытым ключом, такая как RSA и ECC, мгновенно будет сломана.&lt;/p>
&lt;h2 id="появление-pqc-post-quantum-cryptography">Появление PQC (Post-Quantum Cryptography)
&lt;/h2>&lt;p>В ответ на эту беспрецедентную угрозу быстрыми темпами ведутся исследования в области &amp;ldquo;постквантовой криптографии&amp;rdquo; (PQC), основанной на новых математических задачах, которые трудно решить даже на квантовых компьютерах. NIST (Национальный институт стандартов и технологий США) в течение многих лет ведет процесс стандартизации PQC, и в основном рассматриваются следующие математические подходы.&lt;/p>
&lt;h3 id="1-криптография-на-решетках-lattice-based-cryptography">1. Криптография на решетках (Lattice-based Cryptography)
&lt;/h3>&lt;p>В настоящее время это наиболее перспективный подход, который был принят в стандартах алгоритмов NIST (ML-KEM / Kyber, ML-DSA / Dilithium). Он основан на сложности задачи поиска определенной точки в многомерной &amp;ldquo;решетке&amp;rdquo; (Lattice) (например, задача поиска кратчайшего вектора: SVP) и задачи обучения с ошибками (Learning With Errors: LWE).&lt;/p>
&lt;p>Концепция задачи LWE использует то свойство, что если намеренно добавить &amp;ldquo;небольшой шум (ошибку)&amp;rdquo; в систему линейных уравнений, найти решение становится чрезвычайно сложно.
Система уравнений: $\mathbf{A}\mathbf{s} + \mathbf{e} \equiv \mathbf{b} \pmod{q}$
($\mathbf{A}$ и $\mathbf{b}$ публичны, $\mathbf{s}$ — секретный ключ, $\mathbf{e}$ — небольшой шум)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Концептуальный псевдокод задачи LWE (для обучения)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">256&lt;/span> &lt;span class="c1"># Размерность&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3329&lt;/span> &lt;span class="c1"># Модуль&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">512&lt;/span> &lt;span class="c1"># Количество уравнений&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Секретный ключ s и небольшая ошибка e&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Публичная матрица A и публичный вектор b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">A&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">A&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Считается, что восстановить s из A и b очень сложно даже с использованием квантовых компьютеров&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-криптография-на-основе-хешей-hash-based-cryptography">2. Криптография на основе хешей (Hash-based Cryptography)
&lt;/h3>&lt;p>Это схема цифровой подписи, безопасность которой основывается исключительно на стойкости хеш-функций к коллизиям. Поскольку она не имеет математической структуры, она устойчива к квантовым атакам, но размер подписи, как правило, получается большим (например, SPHINCS+).&lt;/p>
&lt;h3 id="3-криптография-на-основе-кодов-code-based-cryptography">3. Криптография на основе кодов (Code-based Cryptography)
&lt;/h3>&lt;p>Метод шифрования, основанный на теории кодов, исправляющих ошибки. Известным примером является криптосистема Мак-Элиса, предложенная в 1978 году. Она имеет долгую историю и признанную надежность, но ее недостатком является очень большой размер открытого ключа (который может достигать нескольких мегабайт).&lt;/p>
&lt;div class="mermaid">timeline
title "История эволюции криптографии и вычислительных машин"
"Древность - Средние века" : "Шифр Цезаря" : "Шифр Виженера" : "Рождение частотного анализа"
"1930-40-е годы" : "Использование и взлом Энигмы" : "Разработка машин Тьюринга и Бомбы"
"1970-е годы" : "Стандартизация DES (1977)" : "Обмен ключами Диффи-Хеллмана (1976)" : "Создание алгоритма RSA (1977)"
"1980-90-е годы" : "Предложение криптографии на эллиптических кривых (ECC)" : "Публикация алгоритма Шора (1994)"
"2000-е годы" : "Стандартизация AES (2001)"
"С 2010-х по настоящее время" : "Ускорение исследований квантовых компьютеров" : "Начало проекта стандартизации PQC от NIST"
"Ближайшее будущее (Q-Day)" : "Создание крупномасштабного квантового компьютера?" : "Полный переход на PQC (ML-KEM/ML-DSA)"&lt;/div>
&lt;hr>
&lt;h1 id="8-заключение-бесконечная-битва-щита-и-меча">8. Заключение: Бесконечная битва щита и меча
&lt;/h1>&lt;p>История криптографии — это история бесконечной борьбы между изобретением новых методов шифрования (щит) и новых методов взлома (меч).&lt;/p>
&lt;p>Шифр Цезаря пал от частотного анализа, а непобедимая Энигма пала перед гениальным умом Тьюринга и силой машин. И теперь даже мощные криптосистемы, такие как RSA и ECC, которые поддерживают основу современного интернет-общества, находятся под угрозой нового &amp;ldquo;меча&amp;rdquo; — квантового компьютера.&lt;/p>
&lt;p>Однако человечество уже смотрит в будущее и готовит новый &amp;ldquo;щит&amp;rdquo; — постквантовую криптографию (PQC). Сегодня обеспечение перехода от существующей криптографии с открытым ключом к PQC (обеспечение криптографической гибкости - Crypto Agility) является насущной задачей для IT-инфраструктуры во всем мире.&lt;/p>
&lt;p>Криптография — это не просто сложная математическая головоломка, это сильнейший барьер для защиты нашей конфиденциальности, нашего имущества и самой социальной инфраструктуры.&lt;/p></description></item><item><title>【Математическое объяснение】Как работает алгоритм RSA, объяснено для старшеклассников</title><link>http://kenji.blog/ru/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/rsa-encryption-math-explained-for-beginners/</guid><description>&lt;img src="http://kenji.blog/p/rsa-encryption-math-explained-for-beginners/img/eyecatch.jpg" alt="Featured image of post 【Математическое объяснение】Как работает алгоритм RSA, объяснено для старшеклассников" />&lt;p>Одной из технологий, фундаментально обеспечивающих безопасность интернет-общества, является алгоритм RSA (шифрование RSA). Оплата кредитными картами в интернет-магазинах, общение с друзьями в соцсетях, отправка и получение конфиденциальной корпоративной информации — большая часть связи, которую мы небрежно используем каждый день, защищена алгоритмом RSA или технологиями-преемниками.&lt;/p>
&lt;p>Однако при слове &amp;ldquo;криптография&amp;rdquo; (шифрование) вы можете представить себе сложные шифровальные машины из шпионских фильмов или сверхсложную математику, понятную лишь избранным гениям. Безусловно, современная теория криптографии основана на продвинутой математике, но &lt;strong>фундаментальный механизм алгоритма RSA вполне можно понять, обладая знаниями математики на уровне старшей школы (свойства целых чисел, простые числа, сравнения по модулю и т.д.)&lt;/strong>.&lt;/p>
&lt;p>В этой статье, взяв за отправную точку знания школьной математики, мы шаг за шагом подробно объясним, на каких математических принципах работает алгоритм RSA и почему его так сложно взломать. Мы будем использовать конкретные примеры и объяснять все так тщательно, чтобы даже те, кто немного не в ладах с математикой, смогли во всем разобраться.&lt;/p>
&lt;hr>
&lt;h2 id="1-симметричное-и-асимметричное-шифрование-с-открытым-ключом">1. Симметричное и асимметричное шифрование (с открытым ключом)
&lt;/h2>&lt;p>Прежде чем переходить к математическим механизмам алгоритма RSA, давайте проясним базовые концепции криптографии. Методы шифрования в целом делятся на два типа: &amp;ldquo;шифрование с симметричным ключом&amp;rdquo; и &amp;ldquo;шифрование с открытым ключом&amp;rdquo; (асимметричное).&lt;/p>
&lt;h3 id="11-ограничения-симметричного-шифрования">1.1 Ограничения симметричного шифрования
&lt;/h3>&lt;p>Большинство шифров, используемых с древних времен, называются &amp;ldquo;системами симметричного шифрования&amp;rdquo;. Это метод, при котором &lt;strong>для &amp;ldquo;шифрования&amp;rdquo; (превращения сообщения в секретный шифр) и &amp;ldquo;расшифрования&amp;rdquo; (возвращения зашифрованного текста в исходное сообщение) используется один и тот же ключ&lt;/strong>.&lt;/p>
&lt;p>Например, предположим, что Алиса отправляет Бобу секретное письмо. Алиса кладет письмо в шкатулку и запирает ее на навесной замок (общий ключ). Чтобы Боб мог открыть эту шкатулку, у него должен быть точно такой же ключ, какой использовала Алиса.&lt;/p>
&lt;p>У этого метода есть серьезная проблема: &amp;ldquo;проблема распределения ключей&amp;rdquo;. Если находящиеся далеко друг от друга Алиса и Боб связываются впервые, как им безопасно обменяться ключами, чтобы их никто не перехватил? Если ключ украдет третье лицо во время пересылки, все последующие зашифрованные сообщения станут достоянием гласности.&lt;/p>
&lt;h3 id="12-революционное-изобретение-шифрование-с-открытым-ключом">1.2 Революционное изобретение: &amp;ldquo;Шифрование с открытым ключом&amp;rdquo;
&lt;/h3>&lt;p>Для решения этой проблемы с распределением ключей была изобретена &amp;ldquo;система шифрования с открытым ключом&amp;rdquo;. Алгоритм RSA относится к этому типу.&lt;/p>
&lt;p>В системе с открытым ключом используются &lt;strong>два разных ключа: &amp;ldquo;ключ для шифрования (открытый ключ)&amp;rdquo; и &amp;ldquo;ключ для расшифрования (закрытый ключ)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>Получатель, Боб, создает пару ключей: &amp;ldquo;открытый ключ&amp;rdquo; и &amp;ldquo;закрытый ключ&amp;rdquo;.&lt;/li>
&lt;li>Боб публикует свой &amp;ldquo;открытый ключ&amp;rdquo; на весь мир (неважно, кто его получит).&lt;/li>
&lt;li>Отправительница, Алиса, использует &amp;ldquo;открытый ключ&amp;rdquo; Боба, чтобы зашифровать сообщение и отправить его.&lt;/li>
&lt;li>Зашифрованное сообщение можно расшифровать только с помощью &amp;ldquo;закрытого ключа&amp;rdquo;, который есть только у Боба.&lt;/li>
&lt;/ol>
&lt;p>Если снова привести аналогию с навесным замком, то Боб создает множество &amp;ldquo;открытых навесных замков (открытых ключей)&amp;rdquo; и разбрасывает их по всему миру. Алиса кладет свое сообщение для Боба в шкатулку, берет один из замков Боба и защелкивает его. Как только замок закрыт, его можно открыть только с помощью &amp;ldquo;мастер-ключа (закрытого ключа)&amp;rdquo;, который есть у Боба. Даже если кто-то украдет шкатулку по дороге, он не сможет ее открыть без мастер-ключа.&lt;/p>
&lt;div class="mermaid">graph TD
A["Алиса (отправитель)"] --> B["Открытый текст (сообщение)"]
B --> C["Процесс шифрования"]
D["Открытый ключ Боба (доступен всем)"] --> C
C --> E["Отправка через интернет: Зашифрованный текст"]
E --> F["Процесс расшифрования"]
G["Закрытый ключ Боба (только у Боба)"] --> F
F --> H["Восстановленный открытый текст (сообщение)"]
H --> I["Боб (получатель)"]&lt;/div>
&lt;p>Чтобы реализовать эту революционную систему, потребовалась своего рода &lt;strong>&amp;ldquo;односторонняя функция (математическая головоломка с односторонним движением)&amp;rdquo;&lt;/strong>, которая позволяет &amp;ldquo;легко зашифровать с помощью открытого ключа, но абсолютно невозможно расшифровать без закрытого&amp;rdquo;. И в качестве деталей для этой головоломки были выбраны хорошо знакомые нам &amp;ldquo;простые числа&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="2-математические-основы-rsa-1-простые-числа-и-факторизация">2. Математические основы RSA 1: Простые числа и факторизация
&lt;/h2>&lt;p>Безопасность алгоритма RSA основана на математическом факте: &lt;strong>&amp;ldquo;факторизация (разложение на простые множители) огромных чисел — чрезвычайно сложная задача&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h3 id="21-что-такое-простые-числа">2.1 Что такое простые числа?
&lt;/h3>&lt;p>Простое число — это &amp;ldquo;натуральное число больше 1, которое делится без остатка только на 1 и на само себя&amp;rdquo;.
Пример: $2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>Простые числа — это словно &amp;ldquo;атомы&amp;rdquo; всех целых чисел. Любое натуральное число можно разложить в виде произведения простых чисел. Это называется &lt;strong>факторизацией (разложением на простые множители)&lt;/strong>. Например, $60 = 2^2 \times 3 \times 5$. Тот факт, что любое число можно разложить на простые множители единственным образом (если не учитывать порядок), известен как &amp;ldquo;основная теорема арифметики&amp;rdquo;.&lt;/p>
&lt;h3 id="22-сложность-факторизации-односторонняя-функция">2.2 Сложность факторизации (Односторонняя функция)
&lt;/h3>&lt;p>Здесь важна асимметрия: &lt;strong>&amp;ldquo;умножать легко, а раскладывать на множители — сложно&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Например, попробуйте вычислить в уме произведение следующих двух простых чисел:
$11 \times 13 = ?$
Это просто. Ответ $143$.&lt;/p>
&lt;p>А как насчет следующего числа?
Разложите на простые множители $323$.
Ну как? Это должно занять некоторое время. (Ответ $17 \times 19$).&lt;/p>
&lt;p>Если числа небольшие, человек еще может как-то справиться, но по мере увеличения чисел вычисления становятся взрывообразно сложными даже для компьютеров. В распространенном сегодня алгоритме RSA используется число $N = p \times q$, полученное путем перемножения двух невероятно огромных простых чисел $p$ и $q$ размером 2048 бит (около 600 цифр в десятичной системе).&lt;/p>
&lt;p>Когда даны два огромных простых числа $p$ и $q$, вычисление $N$ занимает у компьютера долю секунды (меньше миллисекунды). Однако, если дано только $N$, поиск исходных $p$ и $q$ займет столько времени, что даже самые быстрые суперкомпьютеры современности не смогут решить эту задачу и за триллионы лет.&lt;/p>
&lt;p>Эта &lt;strong>&amp;ldquo;вычислительная асимметрия (в одну сторону легко, в обратную — сложно)&amp;rdquo;&lt;/strong> является фундаментом для создания связи между открытым и закрытым ключами.&lt;/p>
&lt;hr>
&lt;h2 id="3-математические-основы-rsa-2-сравнения-арифметика-остатков--модульная-арифметика">3. Математические основы RSA 2: Сравнения (Арифметика остатков / модульная арифметика)
&lt;/h2>&lt;p>Вычисления в алгоритме RSA выполняются не в виде привычных нам сложений и умножений, где числа могут бесконечно расти, а в мире &amp;ldquo;остатков&amp;rdquo; от деления на определенное число. Это называется &lt;strong>сравнением по модулю (модульной арифметикой)&lt;/strong>.&lt;/p>
&lt;h3 id="31-математика-часов">3.1 Математика часов
&lt;/h3>&lt;p>Модульную арифметику часто сравнивают с &amp;ldquo;математикой часов&amp;rdquo;. Предположим, сейчас 10 часов. Который час будет через 5 часов? $10 + 5 = 15$ часов, но на обычных 12-часовых часах мы ответим &amp;ldquo;3 часа&amp;rdquo;. Это потому, что остаток от деления 15 на 12 равен 3.&lt;/p>
&lt;p>В математике это записывается следующим образом:
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
Это читается как &amp;ldquo;15 сравнимо с 3 по модулю 12 (остатки от деления на 12 равны)&amp;rdquo;.&lt;/p>
&lt;h3 id="32-основные-свойства-сравнений">3.2 Основные свойства сравнений
&lt;/h3>&lt;p>Сравнения обладают очень удобными свойствами, похожими на свойства обычных уравнений ($=$). Пусть модуль (делитель) равен $N$.
Если $a \equiv b \pmod N$ и $c \equiv d \pmod N$, то выполняется следующее:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Сложение:&lt;/strong> $a + c \equiv b + d \pmod N$&lt;/li>
&lt;li>&lt;strong>Вычитание:&lt;/strong> $a - c \equiv b - d \pmod N$&lt;/li>
&lt;li>&lt;strong>Умножение:&lt;/strong> $a \times c \equiv b \times d \pmod N$&lt;/li>
&lt;li>&lt;strong>Возведение в степень:&lt;/strong> $a^k \equiv b^k \pmod N$ (где $k$ — натуральное число)&lt;/li>
&lt;/ol>
&lt;p>Особенно важным является свойство &amp;ldquo;возведения в степень&amp;rdquo;. Оно означает, что &lt;strong>&amp;ldquo;остаток от степени равен степени от остатка&amp;rdquo;&lt;/strong>.
Например, мы хотим найти остаток от деления $7^{100}$ на $5$. Честно умножать $7$ само на себя 100 раз, а затем делить на $5$ было бы очень сложно. Но используя свойства сравнений, поскольку $7 \equiv 2 \pmod 5$, мы получаем $7^{100} \equiv 2^{100} \pmod 5$, что позволяет кардинально упростить вычисления. В мире криптографии мы работаем с возведением в степень очень больших чисел, поэтому это свойство просто необходимо.&lt;/p>
&lt;hr>
&lt;h2 id="4-математические-основы-rsa-3-функция-эйлера-и-теорема-эйлера">4. Математические основы RSA 3: Функция Эйлера и теорема Эйлера
&lt;/h2>&lt;p>Здесь начинается магическая математика, составляющая ядро RSA. Появляется &amp;ldquo;теорема Эйлера&amp;rdquo;, которая является обобщением &amp;ldquo;малой теоремы Ферма&amp;rdquo;.&lt;/p>
&lt;h3 id="41-функция-эйлера-тотиент-phin">4.1 Функция Эйлера (тотиент) $\phi(N)$
&lt;/h3>&lt;p>Функция Эйлера (функция $\phi$) для некоторого натурального числа $N$ — это функция, которая возвращает &lt;strong>&amp;ldquo;количество натуральных чисел от 1 до $N$, которые взаимно просты с $N$ (наибольший общий делитель равен 1)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Давайте рассмотрим несколько примеров.&lt;/p>
&lt;ul>
&lt;li>$\phi(5)$: Среди 1, 2, 3, 4, 5 взаимно простыми с 5 являются 1, 2, 3, 4 — всего 4 числа. Следовательно, $\phi(5) = 4$.&lt;/li>
&lt;li>$\phi(6)$: Среди 1, 2, 3, 4, 5, 6 взаимно простыми с 6 являются 1, 5 — всего 2 числа. Следовательно, $\phi(6) = 2$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【Особое свойство для простых чисел】&lt;/strong>
Если $p$ — простое число, все числа от 1 до $p-1$ взаимно просты с $p$. Следовательно,
&lt;/p>
$$ \phi(p) = p - 1 $$
&lt;p>&lt;strong>【Особое свойство для произведения простых чисел】&lt;/strong>
Для двух различных простых чисел $p$ и $q$, если $N = p \times q$, то $\phi(N)$ можно легко вычислить следующим образом:
&lt;/p>
$$ \phi(N) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$
&lt;p>
Это свойство служит &amp;ldquo;потайной дверью&amp;rdquo; (лазейкой) в алгоритме RSA. Тот, кто знает $p$ и $q$ (создатель ключа), может вычислить $\phi(N)$ за мгновение, но третье лицо, знающее только $N$, не сможет вычислить $\phi(N)$ без разложения $N$ на простые множители.&lt;/p>
&lt;h3 id="42-теорема-эйлера">4.2 Теорема Эйлера
&lt;/h3>&lt;p>Леонард Эйлер, используя функцию $\phi(N)$, доказал следующую красивую теорему.&lt;/p>
&lt;p>&lt;strong>Теорема Эйлера:&lt;/strong>
Если целое число $a$ и $N$ взаимно просты, то выполняется следующее сравнение:
&lt;/p>
$$ a^{\phi(N)} \equiv 1 \pmod N $$
&lt;p>Это удивительное свойство: &amp;ldquo;если умножить некоторое число $a$ само на себя $\phi(N)$ раз и разделить на $N$, остаток всегда будет равен $1$&amp;rdquo;. (Если $N$ — простое число $p$, это принимает вид $a^{p-1} \equiv 1 \pmod p$ и называется малой теоремой Ферма).&lt;/p>
&lt;p>Давайте преобразуем эту теорему Эйлера. Умножим обе части на $a$ еще раз.
&lt;/p>
$$ a^{\phi(N) + 1} \equiv a \pmod N $$
&lt;p>Более того, для любого целого числа $k$, $a^{k \cdot \phi(N)}$ также будет равно $1^k = 1$, поэтому выполняется следующее равенство:
&lt;/p>
$$ a^{k \cdot \phi(N) + 1} \equiv a \pmod N $$
&lt;p>Именно эта формула является фундаментальным принципом, на котором строится магия RSA: &lt;strong>&amp;ldquo;после шифрования и расшифрования мы возвращаемся к исходному значению&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="5-алгоритм-rsa-шаги-генерации-ключей-шифрования-и-расшифрования">5. Алгоритм RSA: Шаги генерации ключей, шифрования и расшифрования
&lt;/h2>&lt;p>Теперь, когда у нас есть базовые знания, давайте посмотрим на конкретные шаги алгоритма RSA. Алгоритм RSA можно разделить на три основные фазы: &amp;ldquo;1. Генерация ключей&amp;rdquo;, &amp;ldquo;2. Шифрование&amp;rdquo; и &amp;ldquo;3. Расшифрование&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Выбрать простые числа p, q"] --> A2["Вычислить N = p × q"]
A1 --> A3["Вычислить φ(N) = (p-1)(q-1)"]
A3 --> A4["Выбрать e, взаимно простое с φ(N)"]
A3 --> A5["Вычислить d такое, что e × d ≡ 1 (mod φ(N))"]
A2 --> A6["Открытый ключ (N, e)"]
A4 --> A6
A5 --> A7["Закрытый ключ d"]
B1["2. Открытый текст (сообщение) M"] --> B2["Вычислить C ≡ M^e (mod N)"]
A6 -.-> B2
B2 --> B3["Отправить зашифрованный текст C"]
B3 --> C1["3. Полученный зашифрованный текст C"]
C1 --> C2["Вычислить M ≡ C^d (mod N)"]
A7 -.-> C2
C2 --> C3["Получить исходный открытый текст M"]&lt;/div>
&lt;h3 id="51-генерация-ключей-key-generation">5.1 Генерация ключей (Key Generation)
&lt;/h3>&lt;p>Получатель, Боб, генерирует для себя &amp;ldquo;открытый ключ&amp;rdquo; и &amp;ldquo;закрытый ключ&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Выбор простых чисел:&lt;/strong> Случайным образом выбираются два больших простых числа $p$ и $q$.&lt;/li>
&lt;li>&lt;strong>Вычисление модуля $N$:&lt;/strong> Вычисляется $N = p \times q$. Это $N$ становится публичным.&lt;/li>
&lt;li>&lt;strong>Вычисление $\phi(N)$:&lt;/strong> Вычисляется функция Эйлера $\phi(N) = (p - 1)(q - 1)$. Это секретное число Боба.&lt;/li>
&lt;li>&lt;strong>Выбор открытого ключа $e$:&lt;/strong> Выбирается целое число $e$ такое, что $1 &lt; e &lt; \phi(N)$ и $e$ взаимно просто с $\phi(N)$.&lt;/li>
&lt;li>&lt;strong>Вычисление закрытого ключа $d$:&lt;/strong> Находится целое число $d$, удовлетворяющее следующему условию:
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
Иными словами, &amp;ldquo;$d$ — это такое число, при котором остаток от деления $e \times d$ на $\phi(N)$ равен $1$&amp;rdquo;.&lt;/li>
&lt;/ol>
&lt;p>На этом подготовка ключей завершена.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Открытый ключ:&lt;/strong> Пара $(N, e)$. Публикуется для всего мира.&lt;/li>
&lt;li>&lt;strong>Закрытый ключ:&lt;/strong> $d$. Никогда никому не передается.&lt;/li>
&lt;/ul>
&lt;h3 id="52-шифрование-encryption">5.2 Шифрование (Encryption)
&lt;/h3>&lt;p>Допустим, Алиса хочет отправить Бобу секретное сообщение $M$. ($M$ — это числовое представление текста, причем $0 \le M &lt; N$). Алиса использует открытый ключ Боба $(N, e)$ и выполняет следующие вычисления:&lt;/p>
$$ C \equiv M^e \pmod N $$
&lt;p>Она вычисляет &amp;ldquo;остаток $C$ от деления сообщения $M$ в степени $e$ на $N$&amp;rdquo;. Это $C$ и есть зашифрованный текст.&lt;/p>
&lt;h3 id="53-расшифрование-decryption">5.3 Расшифрование (Decryption)
&lt;/h3>&lt;p>Боб получает зашифрованный текст $C$. Боб использует закрытый ключ $d$ и выполняет следующие вычисления:&lt;/p>
$$ M \equiv C^d \pmod N $$
&lt;p>Если вычислить &amp;ldquo;остаток от деления зашифрованного текста $C$ в степени $d$ на $N$&amp;rdquo;, то удивительным образом восстанавливается исходное сообщение $M$!&lt;/p>
&lt;hr>
&lt;h2 id="6-почему-расшифрование-возвращает-исходное-сообщение-математическое-доказательство">6. Почему расшифрование возвращает исходное сообщение? (Математическое доказательство)
&lt;/h2>&lt;p>Вы можете задаться вопросом: &amp;ldquo;Как так получается, что простое возведение $C$ в степень $d$ возвращает нас к исходному $M$?&amp;rdquo;. Вот тут-то и проявляет свою силу ранее упомянутая &amp;ldquo;теорема Эйлера&amp;rdquo;.&lt;/p>
&lt;p>Давайте подставим формулу шифрования $C = M^e$ в формулу расшифрования $C^d \pmod N$.
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>Здесь вспомните шаг 5 генерации ключей. Боб при создании $d$ выбрал его так, чтобы выполнялось условие $e \times d \equiv 1 \pmod{\phi(N)}$. Это означает, что &amp;ldquo;$ed$ — это число, кратное $\phi(N)$, плюс $1$&amp;rdquo;. Используя целое число $k$, это можно записать следующим образом:
&lt;/p>
$$ ed = k \cdot \phi(N) + 1 $$
&lt;p>Подставим это в показатель степени и разложим с помощью правил возведения в степень:
&lt;/p>
$$ M^{ed} = M^{k \cdot \phi(N) + 1} = M^{k \cdot \phi(N)} \times M^1 = (M^{\phi(N)})^k \times M $$
&lt;p>Если предположить, что сообщение $M$ и $N$ взаимно просты, то согласно &lt;strong>теореме Эйлера&lt;/strong> получаем $M^{\phi(N)} \equiv 1 \pmod N$.
&lt;/p>
$$ (M^{\phi(N)})^k \times M \equiv 1^k \times M \equiv M \pmod N $$
&lt;p>Следовательно, блестяще выполняется следующая формула:
&lt;/p>
$$ C^d \equiv M \pmod N $$
&lt;p>Алиса не знает $d$, перехватчик тоже не знает $d$, поэтому получить $M$ из $C$ может только Боб, владеющий ключом $d$.&lt;/p>
&lt;hr>
&lt;h2 id="7-конкретный-пример-давайте-испытаем-rsa-вручную-используя-небольшие-простые-числа">7. Конкретный пример: давайте испытаем RSA вручную, используя небольшие простые числа
&lt;/h2>&lt;p>Давайте попробуем выполнить зашифрованную связь от Алисы к Бобу на практике, используя небольшие числа (простые числа).&lt;/p>
&lt;p>&lt;strong>【Фаза генерации ключей Боба】&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Выбираем два простых числа $p=11$, $q=13$.&lt;/li>
&lt;li>Вычисляем $N = 11 \times 13 = 143$.&lt;/li>
&lt;li>Вычисляем $\phi(N) = (11 - 1) \times (13 - 1) = 10 \times 12 = 120$.&lt;/li>
&lt;li>Выбираем открытый ключ $e$, взаимно простой с $\phi(N)=120$. Пусть $e=7$.&lt;/li>
&lt;li>Находим закрытый ключ $d$. Ищем такое $d$, чтобы выполнялось $7 \times d \equiv 1 \pmod{120}$.
В уравнении $7d = 120k + 1$, если $k=6$, получаем $721$, а $721 \div 7 = 103$.
Следовательно, $d = 103$.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Открытый ключ: $(N=143, e=7)$&lt;/li>
&lt;li>Закрытый ключ: $d=103$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【Фаза шифрования Алисы】&lt;/strong>
Предположим, мы хотим отправить сообщение $M = 9$.
Формула: $C \equiv 9^7 \pmod{143}$
$9^7 = 4,782,969$. Если разделить это число на 143, получим $33447$ с остатком $48$.
Зашифрованный текст получился $C = 48$.&lt;/p>
&lt;p>&lt;strong>【Фаза расшифрования Боба】&lt;/strong>
Боб получает зашифрованный текст $C = 48$ и использует закрытый ключ $d = 103$ для его расшифрования.
Формула: $M \equiv 48^{103} \pmod{143}$
Если запустить на калькуляторе или в программе &lt;code>(48 ** 103) % 143&lt;/code>, результат блестяще окажется равным &amp;ldquo;&lt;strong>9&lt;/strong>&amp;rdquo;! Исходное сообщение успешно получено.&lt;/p>
&lt;hr>
&lt;h2 id="8-как-найти-закрытый-ключ-d-расширенный-алгоритм-евклида">8. Как найти закрытый ключ $d$: Расширенный алгоритм Евклида
&lt;/h2>&lt;p>В примере с ручными вычислениями мы нашли $d=103$, подобрав $k$ интуитивно, но если числа состоят из сотен цифр, этот метод не сработает. В реальных программах используется алгоритм, называемый &lt;strong>&amp;ldquo;расширенным алгоритмом Евклида&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Решить $7d \equiv 1 \pmod{120}$ — это то же самое, что найти целые числа $d$ и $y$, удовлетворяющие уравнению $7d + 120y = 1$. Выполняя алгоритм Евклида в обратном порядке, это можно вычислить механически.&lt;/p>
&lt;ol>
&lt;li>$120 \div 7 = 17$, остаток $1$&lt;/li>
&lt;li>Если преобразовать это, получим $1 = 120 - 17 \times 7$&lt;/li>
&lt;li>То есть, $-17 \times 7 \equiv 1 \pmod{120}$&lt;/li>
&lt;/ol>
&lt;p>В мире по модулю $120$, число $-17$ имеет то же значение, что и $120 - 17 = 103$. Следовательно, мы мгновенно находим $d = 103$. Этот метод позволяет выполнять вычисления очень быстро даже для огромных чисел.&lt;/p>
&lt;hr>
&lt;h2 id="9-другое-лицо-rsa-цифровая-подпись">9. Другое лицо RSA: Цифровая подпись
&lt;/h2>&lt;p>Замечательной особенностью алгоритма RSA является то, что, поменяв ролями открытый и закрытый ключи, его можно использовать как &lt;strong>&amp;ldquo;цифровую подпись&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>При шифровании порядок был таким: &amp;ldquo;шифрование открытым ключом $\Rightarrow$ расшифрование закрытым ключом&amp;rdquo;,
а в цифровой подписи выполняется процедура &amp;ldquo;шифрование закрытым ключом $\Rightarrow$ расшифрование открытым ключом&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Алиса создает подпись с помощью закрытого ключа"] --> A2["S ≡ M^d (mod N)"]
A2 --> A3["Отправить сообщение M и подпись S"]
A3 --> B1["2. Боб проверяет подпись с помощью открытого ключа"]
B1 --> B2["Вычислить M' ≡ S^e (mod N)"]
B2 --> B3["Проверить, совпадают ли M' и M"]&lt;/div>
&lt;p>Алиса использует свой закрытый ключ $d$ для преобразования сообщения (это и есть подпись $S$) и отправляет его Бобу. Боб использует открытый ключ Алисы $e$ для выполнения проверочных вычислений. Если результат вычислений совпадает с исходным сообщением, это одновременно доказывает, что &amp;ldquo;эти данные могли быть созданы только с помощью закрытого ключа Алисы&amp;rdquo; и что &amp;ldquo;сообщение не было изменено в пути&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="10-rsa-на-практике-в-коде">10. RSA на практике в коде
&lt;/h2>&lt;p>Возведение в степень, которое сложно сделать вручную, очень легко реализовать с помощью Python. Ниже представлен код на Python, позволяющий опробовать основную логику алгоритма RSA.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Найти наибольший общий делитель&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Найти закрытый ключ d (используется встроенная функция Python 3.8 и выше)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Генерация ключей&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Открытый ключ: (N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">), Закрытый ключ: d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Шифрование&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Зашифрованный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Расшифрование&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">decrypted_message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Расшифрованное сообщение: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Функция &lt;code>pow(base, exp, mod)&lt;/code> в Python внутри использует быстрый алгоритм, называемый &amp;ldquo;алгоритмом возведения в степень справа налево&amp;rdquo; (быстрое возведение в степень по модулю), поэтому вычисления для чисел из сотен цифр завершаются мгновенно.&lt;/p>
&lt;hr>
&lt;h2 id="11-заключение-и-будущее-криптографии">11. Заключение и будущее криптографии
&lt;/h2>&lt;p>Основываясь на знаниях школьной математики, мы разобрали механизмы работы алгоритма RSA.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Сложность факторизации:&lt;/strong> Легко вычислить $p \times q = N$, но очень сложно найти $p, q$ по заданному $N$.&lt;/li>
&lt;li>&lt;strong>Сравнения и теорема Эйлера:&lt;/strong> Благодаря закону $a^{\phi(N)} \equiv 1 \pmod N$ создается волшебная потайная дверь, позволяющая &amp;ldquo;вернуться к исходному значению при возведении в определенную степень&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Открытый и закрытый ключи:&lt;/strong> Зашифровать может кто угодно, но расшифровать способен только законный получатель.&lt;/li>
&lt;/ol>
&lt;p>Используемое в настоящее время в алгоритме RSA число $N$ состоит из более чем 600 цифр, и даже если задействовать все суперкомпьютеры в мире, разложение его на множители займет больше времени, чем возраст Вселенной. Однако, когда &amp;ldquo;квантовые компьютеры&amp;rdquo;, исследования которых ведутся в последние годы, будут введены в практическое использование в будущем, с помощью &amp;ldquo;алгоритма Шора&amp;rdquo; эту задачу факторизации можно будет решить за мгновение. По этой причине во всем мире сейчас ускоренными темпами ведется разработка &amp;ldquo;постквантовой криптографии&amp;rdquo;, которая не может быть взломана даже квантовыми компьютерами.&lt;/p>
&lt;p>Продвинутая математика, которая часто считается &amp;ldquo;бесполезной&amp;rdquo;, на самом деле фундаментально защищает нашу повседневную жизнь. Алгоритм RSA — это прекрасный учебный материал, который демонстрирует глубину и красоту математики. Мы надеемся, что благодаря этой статье вы смогли хотя бы немного почувствовать, насколько интересными могут быть криптография и математика.&lt;/p></description></item><item><title>Что такое полностью гомоморфное шифрование (FHE)? Объяснение основы безопасности следующего поколения</title><link>http://kenji.blog/ru/p/fully-homomorphic-encryption-fhe-explained/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/fully-homomorphic-encryption-fhe-explained/</guid><description>&lt;img src="http://kenji.blog/p/fully-homomorphic-encryption-fhe-explained/img/eyecatch.jpg" alt="Featured image of post Что такое полностью гомоморфное шифрование (FHE)? Объяснение основы безопасности следующего поколения" />&lt;p>В условиях, когда облачные вычисления и технологии ИИ становятся основой общества, компромисс между «конфиденциальностью данных» и «использованием данных» является одной из важнейших проблем. Растет спрос на использование ИИ в облаке для анализа высококонфиденциальных данных, таких как медицинские данные, финансовая информация и личная биометрическая информация, но многие компании не решаются передавать данные наружу из-за соображений безопасности.&lt;/p>
&lt;p>Традиционные технологии шифрования (такие как AES и RSA) отлично справляются с защитой данных, хранящихся в хранилище (Data at Rest) и передаваемых по сети (Data in Transit). Однако при &lt;strong>выполнении обработки (вычислений) на стороне сервера, например, поиска или машинного обучения над данными (Data in Use), необходимо сначала расшифровать их и вернуть в виде открытого текста&lt;/strong>. Если сервер будет взломан в момент расшифровки, или если злонамеренный внутренний администратор заглянет в данные, это приведет к прямой утечке информации.&lt;/p>
&lt;p>Технология мечты, которая преодолевает эту фундаментальную слабость «расшифровки во время обработки», — это &lt;strong>полностью гомоморфное шифрование (Fully Homomorphic Encryption: FHE)&lt;/strong>. Использование FHE позволяет выполнять вычислительную обработку данных, оставляя их зашифрованными без какой-либо расшифровки, и возвращать клиенту только зашифрованный результат.&lt;/p>
&lt;p>В этой статье мы глубоко и детально рассмотрим FHE, основу безопасности следующего поколения, начиная от концепции и истории, новаторского прорыва Крейга Джентри, математических основ (таких как Ring-LWE), до самой большой проблемы — «шума» — и ее решения (bootstrapping, начальная загрузка/загрузка), а также новейших библиотек реализаций.&lt;/p>
&lt;hr>
&lt;h2 id="1-что-такое-гомоморфное-шифрование-основные-концепции">1. Что такое гомоморфное шифрование? Основные концепции
&lt;/h2>&lt;p>«Гомоморфный» — это алгебраический термин, который относится к свойству отображения между множествами с определенной структурой, при котором сохраняется структура операций. «Гомоморфизм» в теории криптографии означает свойство, при котором &lt;strong>операции в пространстве открытого текста соответствуют операциям в пространстве шифротекста&lt;/strong>.&lt;/p>
&lt;p>Выражаясь простой математической формулой, пусть $E(\cdot)$ будет функцией шифрования, а $D(\cdot)$ — функцией дешифрования для открытых текстов $m_1$ и $m_2$. Если операция над открытым текстом (например, сложение или умножение) обозначается как $\circ$, а операция над шифротекстом — как $\diamond$, то выполняется следующее соотношение:&lt;/p>
$$ D(E(m_1) \diamond E(m_2)) = m_1 \circ m_2 $$
&lt;p>Другими словами, расшифровка результата применения какой-либо операции $\diamond$ к шифротекстам $E(m_1)$ и $E(m_2)$ совпадает с результатом применения операции $\circ$ к исходным открытым текстам.&lt;/p>
&lt;h3 id="поток-данных-в-облачных-вычислениях">Поток данных в облачных вычислениях
&lt;/h3>&lt;p>Архитектура облачной обработки с использованием FHE полностью отличается от традиционной. На следующем рисунке показан поток безопасной обработки данных с использованием FHE.&lt;/p>
&lt;div class="mermaid">graph TD
A["Клиент (хранит закрытый ключ)"] -->|1. Шифрует открытый текст x: E(x)| B["Облачный сервер (только зашифрованные данные)"]
B -->|2. Применяет функцию f к шифротексту: E(f(x))| B
B -->|3. Зашифрованный результат вычислений E(y)| A
A -->|4. Расшифровывает закрытым ключом: y = f(x)| A
style A fill:#d4edda,stroke:#28a745
style B fill:#f8d7da,stroke:#dc3545&lt;/div>
&lt;p>Сервер получает зашифрованные данные $E(x)$, но, поскольку у него нет закрытого ключа, он абсолютно не может знать содержимое данных. Однако, используя свойства FHE, можно применить функцию $f$ (например, модель машинного обучения) к шифротексту и сгенерировать $E(f(x))$. Клиент получает это и расшифровывает с помощью своего собственного закрытого ключа, получая таким образом желаемый результат $y = f(x)$.&lt;/p>
&lt;hr>
&lt;h2 id="2-история-эволюции-гомоморфного-шифрования-phe-she-fhe">2. История эволюции гомоморфного шифрования: PHE, SHE, FHE
&lt;/h2>&lt;p>Гомоморфное шифрование не сразу приняло свою нынешнюю «полную» форму. Оно широко подразделяется на три этапа в зависимости от типов и количества операций, которые могут быть выполнены.&lt;/p>
&lt;h3 id="partially-homomorphic-encryption-phe-частично-гомоморфное-шифрование">Partially Homomorphic Encryption (PHE: Частично гомоморфное шифрование)
&lt;/h3>&lt;p>PHE — это схема шифрования, которая может выполнять &lt;strong>только одну&lt;/strong> операцию, либо сложение, либо умножение, неограниченное количество раз. На самом деле, шифры с таким свойством существуют с давних времен.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Шифрование RSA (гомоморфизм по умножению)&lt;/strong>
Шифрование RSA непреднамеренно обладало гомоморфизмом по умножению. Пусть открытые тексты равны $m_1, m_2$, а открытый ключ — $(e, N)$:
$$ E(m_1) = m_1^e \pmod N $$
$$ E(m_2) = m_2^e \pmod N $$
Если перемножить их, то получится:
$$ E(m_1) \times E(m_2) = (m_1 \cdot m_2)^e \pmod N = E(m_1 \times m_2) $$
Таким образом, умножение шифротекстов соответствует умножению открытых текстов.&lt;/li>
&lt;li>&lt;strong>Шифрование Пэйе (гомоморфизм по сложению)&lt;/strong>
Шифрование Пэйе (Paillier), изобретенное в 1999 году, обладает гомоморфизмом по сложению. Оно применяется на практике, например, в электронном голосовании (подсчет зашифрованных голосов и расшифровка только конечного результата).&lt;/li>
&lt;/ul>
&lt;h3 id="somewhat-homomorphic-encryption-she-ограниченно-гомоморфное-шифрование">Somewhat Homomorphic Encryption (SHE: Ограниченно гомоморфное шифрование)
&lt;/h3>&lt;p>Это схема, которая может выполнять &lt;strong>обе&lt;/strong> операции, сложение и умножение, но имеет &lt;strong>ограничение на количество выполняемых операций (глубину схемы)&lt;/strong>. Из-за накопления «шума», о котором будет рассказано позже, дешифрование становится невозможным после определенного количества умножений. К этой категории относится шифр BGN (Boneh-Goh-Nissim) 2005 года, но у него были ограничения для выполнения практических сложных вычислений (таких как глубокое обучение).&lt;/p>
&lt;h3 id="fully-homomorphic-encryption-fhe-полностью-гомоморфное-шифрование">Fully Homomorphic Encryption (FHE: Полностью гомоморфное шифрование)
&lt;/h3>&lt;p>Это схема шифрования, которая позволяет выполнять как сложение, так и умножение &lt;strong>неограниченное количество раз&lt;/strong>. Подобно полноте по Тьюрингу в теории информации, если сложение (соответствует XOR) и умножение (соответствует AND) можно комбинировать бесконечно, это означает, что в принципе любая вычислимая функция или алгоритм могут быть выполнены в зашифрованном виде.&lt;/p>
&lt;p>FHE долгое время называли «Святым Граалем криптографии» и считали невозможным. Однако в 2009 году &lt;strong>Крейг Джентри&lt;/strong>, в то время аспирант Стэнфордского университета, предложил первую схему FHE с использованием идеальных решеток (Ideal Lattices), что потрясло мир.&lt;/p>
&lt;hr>
&lt;h2 id="3-математические-основы-fhe-задача-lwe-и-ring-lwe">3. Математические основы FHE: Задача LWE и Ring-LWE
&lt;/h2>&lt;p>Многие из современных господствующих схем FHE основаны на &lt;strong>задаче LWE (Обучение с ошибками, Learning With Errors)&lt;/strong>, сложной математической проблеме в «криптографии на решетках (Lattice-based Cryptography)», которая также известна как постквантовая криптография.&lt;/p>
&lt;h3 id="интуитивное-понимание-задачи-lwe">Интуитивное понимание задачи LWE
&lt;/h3>&lt;p>Решение системы линейных уравнений является простой задачей, если использовать такие методы, как метод исключения Гаусса.&lt;/p>
$$ \begin{cases} 3s_1 + 4s_2 + 2s_3 \equiv 12 \pmod{17} \\ 1s_1 + 9s_2 + 5s_3 \equiv 8 \pmod{17} \\ \vdots \end{cases} $$
&lt;p>Но что произойдет, если к результатам этих уравнений добавить крошечную «случайную ошибку (шум)» $e$?&lt;/p>
$$ \begin{cases} 3s_1 + 4s_2 + 2s_3 + e_1 \equiv 13 \pmod{17} \\ 1s_1 + 9s_2 + 5s_3 + e_2 \equiv 7 \pmod{17} \\ \vdots \end{cases} $$
&lt;p>Добавление только этой ошибки $e$ превращает задачу нахождения секретного вектора переменных $\vec{s}$ в NP-трудную задачу, которую трудно взломать даже с помощью современных суперкомпьютеров или квантовых компьютеров. Это и есть задача LWE.&lt;/p>
&lt;h3 id="задача-ring-lwe-rlwe">Задача Ring-LWE (RLWE)
&lt;/h3>&lt;p>Стандартная задача LWE включает в себя матричные операции, поэтому она имела проблему огромных размеров ключей (которые могли достигать гигабайт) и низкой вычислительной эффективности. Для решения этой проблемы была введена &lt;strong>задача Ring-LWE (RLWE)&lt;/strong>, которая использует операции над кольцами многочленов.&lt;/p>
&lt;p>В RLWE элементы принадлежат кольцу многочленов $R_q = \mathbb{Z}_q[x] / (x^N + 1)$ (где $N$ — степень двойки, а $q$ — простое число, выступающее в качестве модуля).
Если закрытый ключ является многочленом $s(x)$, а случайный многочлен — $a(x)$, и небольшой многочлен шума — $e(x)$, то открытый ключ будет представлять собой следующую пару:&lt;/p>
$$ (a(x), b(x)) \quad \text{where} \quad b(x) = -a(x) \cdot s(x) + e(x) \pmod q $$
&lt;p>При шифровании открытый текст $m(x)$ кодируется с использованием свойств этого многочлена для генерации шифротекста.&lt;/p>
&lt;hr>
&lt;h2 id="4-самое-большое-препятствие-шум-и-bootstrapping-джентри">4. Самое большое препятствие «Шум» и Bootstrapping Джентри
&lt;/h2>&lt;p>Самая важная концепция в понимании FHE — это &lt;strong>«управление шумом»&lt;/strong>.&lt;/p>
&lt;p>Шифры на основе LWE/RLWE преднамеренно включают небольшой «шум (ошибку)» для обеспечения безопасности.
Процесс дешифрования шифротекста $c$ открытого текста $m$, грубо говоря, выражается следующей формулой:&lt;/p>
$$ D(c) = (c \cdot s) \pmod q = m + \text{noise} $$
&lt;p>При дешифровании этот &lt;code>noise&lt;/code> (шум) удаляется посредством округления, в результате чего получается правильный открытый текст $m$. Однако при выполнении гомоморфных операций (особенно умножения) между шифротекстами этот шум резко возрастает.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Гомоморфное сложение&lt;/strong>: Шум увеличивается аддитивно ($e_1 + e_2$). Это относительно постепенное увеличение.&lt;/li>
&lt;li>&lt;strong>Математическое выражение гомоморфизма посредством гомоморфного сложения&lt;/strong>:
$$ E(m_1) \oplus E(m_2) = E(m_1 + m_2) $$&lt;/li>
&lt;li>&lt;strong>Гомоморфное умножение&lt;/strong>: Шум возрастает мультипликативно взрывным образом (поскольку он включает $e_1 \times e_2$ и т. д.). После всего нескольких умножений шум превышает порог $q/2$, в результате чего невозможно выполнить правильное округление, и дешифрование завершается неудачей.&lt;/li>
&lt;li>&lt;strong>Математическое выражение гомоморфизма посредством гомоморфного умножения&lt;/strong>:
$$ E(m_1) \otimes E(m_2) = E(m_1 \times m_2) $$&lt;/li>
&lt;/ul>
&lt;p>Именно поэтому FHE долгое время не удавалось реализовать, и все ограничивалось SHE (с ограничением количества операций).&lt;/p>
&lt;h3 id="магия-bootstrapping-перезагрузки">Магия Bootstrapping (перезагрузки)
&lt;/h3>&lt;p>Гениальный вклад Крейга Джентри заключается в изобретении метода снижения шума, называемого &lt;strong>«bootstrapping»&lt;/strong>. Это был сдвиг парадигмы в криптографии.&lt;/p>
&lt;p>Интуитивно это операция: «прежде чем шифротекст будет поврежден, наполнившись шумом, &amp;lsquo;расшифровать&amp;rsquo; его, оставаясь в зашифрованном состоянии, очистить его и поместить в новый шифротекст».&lt;/p>
&lt;ol>
&lt;li>Допустим, есть шифротекст с высоким уровнем шума $C_{noisy}$.&lt;/li>
&lt;li>Клиент заранее передает серверу «зашифрованный с помощью открытого ключа» закрытый ключ $sk$, $E_{pk}(sk)$ (называемый ключом bootstrapping).&lt;/li>
&lt;li>Сервер гомоморфно выполняет &lt;strong>схему дешифрования (Decryption Circuit)&lt;/strong> над $C_{noisy}$.&lt;/li>
&lt;li>В частности, он выполняет «дешифрование в зашифрованном пространстве» для $E_{pk}(C_{noisy})$ с использованием $E_{pk}(sk)$.&lt;/li>
&lt;li>Поскольку сама эта схема дешифрования также является гомоморфной операцией, она генерирует новый шум, но шум результирующего нового шифротекста $C_{fresh}$ сбрасывается до определенного «фиксированного уровня».&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["Шифротекст с высоким шумом C_noisy"] --> B["Гомоморфная схема дешифрования (Eval_Dec)"]
C["Зашифрованный закрытый ключ E(sk)"] --> B
B --> D["Шифротекст с низким шумом C_fresh"]
style B fill:#ffeeba,stroke:#ffc107&lt;/div>
&lt;p>Периодически выполняя этот bootstrapping в процессе вычислений, теоретически стало возможным вычислять схемы бесконечной глубины (достижение FHE). Однако в первоначальном методе Джентри эта операция bootstrapping занимала от нескольких десятков минут до нескольких часов на один раз, что было катастрофически затратно в вычислительном плане.&lt;/p>
&lt;hr>
&lt;h2 id="5-поколения-fhe-и-эволюция-основных-схем">5. Поколения FHE и эволюция основных схем
&lt;/h2>&lt;p>В целях практического применения FHE криптографы со всего мира соревновались в совершенствовании алгоритмов. В настоящее время FHE классифицируется в основном на четыре поколения/семейства.&lt;/p>
&lt;h3 id="второе-поколение-точные-операции-над-целыми-числами-bgv-bfv">Второе поколение: Точные операции над целыми числами (BGV, BFV)
&lt;/h3>&lt;p>Схемы &lt;strong>BGV (Brakerski-Gentry-Vaikuntanathan)&lt;/strong> и &lt;strong>BFV (Brakerski/Fan-Vercauteren)&lt;/strong> появились в 2011–2012 годах. Они основаны на RLWE и подходят для модульных арифметических операций над целыми числами (точные вычисления).
Они поддерживают методы пакетирования (Batching), такие как SIMD (Single Instruction, Multiple Data), и характеризуются способностью упаковывать тысячи слотов данных в один огромный полиномиальный шифротекст и вычислять их параллельно за один раз.&lt;/p>
&lt;h3 id="третье-поколение-ускорение-bootstrapping-gsw-fhew-tfhe">Третье поколение: Ускорение Bootstrapping (GSW, FHEW, TFHE)
&lt;/h3>&lt;p>Схема &lt;strong>GSW (Gentry-Sahai-Waters)&lt;/strong> 2013 года упростила структуру FHE. Ее развитием является &lt;strong>TFHE (Fast Fully Homomorphic Encryption over the Torus)&lt;/strong>, одно из современных основных направлений.
Особенностью TFHE является чрезвычайно быстрый bootstrapping (в миллисекундах). Он эффективен при выполнении операций на уровне логических вентилей (логические схемы, такие как AND, XOR) и имеет относительно небольшой размер шифротекста, что делает его подходящим для быстрой оценки любых логических схем.&lt;/p>
&lt;h3 id="четвертое-поколение-специализация-на-приближенных-вычислениях-и-машинном-обучении-ckks">Четвертое поколение: Специализация на приближенных вычислениях и машинном обучении (CKKS)
&lt;/h3>&lt;p>Схема &lt;strong>CKKS (Cheon-Kim-Kim-Song)&lt;/strong>, предложенная Cheon и соавторами в 2017 году, может считаться окончательной технологией для защиты конфиденциальности в современном ИИ и машинном обучении.
В то время как предыдущие FHE фокусировались на «точных целочисленных вычислениях», CKKS поддерживает &lt;strong>«приближенные вычисления чисел с плавающей запятой»&lt;/strong> в зашифрованном виде. Он демонстрирует невероятную производительность в вычислениях над вещественными числами, где допускаются небольшие ошибки, таких как обучение и логический вывод нейронных сетей.&lt;/p>
&lt;p>В следующей таблице обобщено, как выбрать схему в зависимости от цели.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Имя схемы&lt;/th>
&lt;th style="text-align:left">Подходящий тип данных&lt;/th>
&lt;th style="text-align:left">Рекомендуемые варианты использования&lt;/th>
&lt;th style="text-align:left">Особенности&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>BFV / BGV&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Целые числа (Integer)&lt;/td>
&lt;td style="text-align:left">Точные статистические расчеты, агрегация финансовых данных, поиск в БД&lt;/td>
&lt;td style="text-align:left">Высокая пропускная способность за счет пакетирования SIMD&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>CKKS&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Вещественные числа (Real/Complex)&lt;/td>
&lt;td style="text-align:left">Машинное обучение (DNN, логистическая регрессия), обработка сигналов&lt;/td>
&lt;td style="text-align:left">Ускорение за счет приближенных вычислений, масштабирование (rescaling)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>TFHE&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Булевы значения (Boolean)&lt;/td>
&lt;td style="text-align:left">Любые логические схемы, поиск по строкам, оценка нелинейных функций&lt;/td>
&lt;td style="text-align:left">Сверхбыстрый bootstrapping (на уровне миллисекунд)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="6-практика-библиотеки-fhe-и-концептуальный-код">6. Практика: Библиотеки FHE и концептуальный код
&lt;/h2>&lt;p>В настоящее время доступно множество библиотек с открытым исходным кодом, которые позволяют использовать FHE без глубоких знаний криптографии.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Microsoft SEAL (Simple Encrypted Arithmetic Library)&lt;/strong>: Библиотека C++, поддерживающая BFV, BGV и CKKS. Один из отраслевых стандартов. Ее привязка для Python, &lt;strong>TenSEAL&lt;/strong>, популярна среди инженеров ИИ.&lt;/li>
&lt;li>&lt;strong>Zama (Concrete)&lt;/strong>: Фреймворк на базе TFHE. Может быть написан на Rust/Python, предоставляет функциональность (Concrete ML) для компиляции существующих моделей PyTorch для работы поверх FHE.&lt;/li>
&lt;li>&lt;strong>OpenFHE&lt;/strong>: Преемник PALISADE, комплексная библиотека C++, поддерживающая все основные схемы.&lt;/li>
&lt;/ul>
&lt;h3 id="пример-программирования-fhe-на-python-tenseal">Пример программирования FHE на Python (TenSEAL)
&lt;/h3>&lt;p>Здесь показан концептуальный пример кода на Python, использующий схему CKKS для сложения и умножения векторов вещественных чисел в зашифрованном виде.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">tenseal&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">ts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Настройка контекста (включая генерацию ключей)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Использование схемы CKKS и установка степени полинома 8192&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SCHEME_TYPE&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CKKS&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">poly_modulus_degree&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8192&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coeff_mod_bit_sizes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">60&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">60&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_galois_keys&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">global_scale&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="mi">40&lt;/span> &lt;span class="c1"># Коэффициент масштабирования для вещественных чисел&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Сторона клиента: Шифрование данных&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vector1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">1.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">2.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vector2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">2.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">4.0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Преобразование векторов открытого текста в шифротекст (Обычно выполняется на стороне клиента)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_v1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ckks_vector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_v2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ckks_vector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Сторона сервера: Операции в зашифрованном виде (Защита Data in Use)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Сервер не знает открытого текста, но может выполнять сложение и умножение&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_v1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">enc_v2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">enc_mul&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_v1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">enc_v2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Сторона клиента: Дешифрование результатов&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Только клиент с закрытым ключом может видеть результат&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res_add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_add&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decrypt&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res_mul&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">enc_mul&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decrypt&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Расшифрованный результат сложения: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">res_add&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример вывода: [3.5000001, 5.5000001, 7.5000002] (Включает небольшие ошибки из-за приближенных вычислений)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Расшифрованный результат умножения: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">res_mul&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример вывода: [3.0000002, 7.5000005, 14.0000003]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Как видно из приведенного выше кода, можно интуитивно описывать вычисления между шифротекстами путем перегрузки обычных операторов Python, например, &lt;code>enc_v1 + enc_v2&lt;/code>. На стороне сервера векторные операции завершаются без знания содержимого векторов.&lt;/p>
&lt;hr>
&lt;h2 id="7-проблемы-fhe-производительность-и-аппаратное-ускорение">7. Проблемы FHE: Производительность и аппаратное ускорение
&lt;/h2>&lt;p>Хотя FHE обеспечивает теоретически идеальную безопасность, самой большой проблемой на пути к практическому применению являются &lt;strong>«накладные расходы на производительность»&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Вычислительные накладные расходы&lt;/strong>: По сравнению с вычислениями с открытым текстом вычисления с шифротекстом на CPU медленнее в тысячи и десятки тысяч раз. Умножение полиномов и bootstrapping требуют огромных вычислений БПФ (быстрого преобразования Фурье) и NTT (теоретико-числового преобразования).&lt;/li>
&lt;li>&lt;strong>Увеличение размера данных (Ciphertext Expansion)&lt;/strong>: Несколько байтов открытого текста при шифровании могут превратиться в несколько мегабайтов. Это создает большую нагрузку на пропускную способность памяти и сети.&lt;/li>
&lt;/ol>
&lt;h3 id="подходы-к-аппаратному-решению">Подходы к аппаратному решению
&lt;/h3>&lt;p>Чтобы преодолеть эти накладные расходы, по всему миру разрабатываются аппаратные ускорители (соответствующие ASIC, FPGA, GPU), специально предназначенные для FHE.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Ускорение на GPU&lt;/strong>: Ведутся работы по распараллеливанию вычислений NTT и bootstrapping с использованием мощных графических процессоров, таких как от NVIDIA, и сообщается об ускорении в десятки раз по сравнению с программными реализациями (например, 100x.ai, TFHE-rs CUDA backend от Zama).&lt;/li>
&lt;li>&lt;strong>Проект DARPA DPRIVE&lt;/strong>: Агентство передовых исследований проектов обороны США (DARPA) продвигает проект разработки специализированного аппаратного обеспечения «DPRIVE (Data Protection in Virtual Environments)», чтобы повысить скорость вычислений FHE до уровня скорости обработки открытого текста (с накладными расходами в пределах 10-кратного увеличения), в котором участвуют Intel, Microsoft, Intellectual Ventures и другие.&lt;/li>
&lt;li>&lt;strong>Появление FPU (FHE Processing Unit)&lt;/strong>: Стартапы, такие как Cornami и Optalysys, приступили к разработке специализированных чипов для FHE с использованием оптических вычислений и специальной кремниевой архитектуры.&lt;/li>
&lt;/ul>
&lt;p>Возможно, в ближайшем будущем наступит эпоха, когда «FPU» будут стандартно устанавливаться в серверах и облачной инфраструктуре, подобно NPU (Neural Processing Unit) в ИИ.&lt;/p>
&lt;hr>
&lt;h2 id="8-ожидаемые-варианты-использования">8. Ожидаемые варианты использования
&lt;/h2>&lt;p>Теперь, когда FHE приближается к практической скорости, ожидаются прорывные инновации в следующих областях.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Защита конфиденциальности в медицинском и геномном анализе&lt;/strong>:
Обучая ИИ в облаке на медицинских картах пациентов или данных ДНК из нескольких больниц в зашифрованном с помощью FHE виде, можно разрабатывать высокоточные модели диагностики рака или новые лекарства без нарушения законов о конфиденциальности (HIPAA или GDPR).&lt;/li>
&lt;li>&lt;strong>Выявление мошенничества и борьба с отмыванием денег (AML) в финансовых учреждениях&lt;/strong>:
Конкурирующие банки могут сверять данные друг друга в зашифрованном состоянии, не раскрывая информацию о счетах клиентов или историю транзакций, обеспечивая межбанковский анализ для выявления огромных сетей мошеннических переводов.&lt;/li>
&lt;li>&lt;strong>Безопасный API вывода ИИ (MaaS: Model as a Service)&lt;/strong>:
Пользователи шифруют свой голос, изображения лиц или промпты и отправляют их в сервисы ИИ (такие как LLM, например, ChatGPT). Провайдер ИИ генерирует ответ, не зная ввода пользователя, и возвращает его в виде шифротекста. Это полностью устраняет опасения, что «ИИ может изучить или украсть личную информацию».&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="9-заключение-будущее-криптографии-лежит-в-невидимых-вычислениях">9. Заключение: Будущее криптографии лежит в «Невидимых вычислениях»
&lt;/h2>&lt;p>Точно так же, как изобретение криптографии с открытым ключом (RSA) в 1970-х годах сделало возможной безопасную связь в Интернете (например, HTTPS), изобретение FHE Крейгом Джентри является одной из самых важных вех в истории криптографии.&lt;/p>
&lt;p>Сегодня полностью гомоморфное шифрование (FHE) вышло из лабораторной теории, и Microsoft, IBM, Intel, Google и множество стартапов вступили в стадию жесткой конкуренции за его практическое применение. Проблемы с вычислительными затратами и размером данных по-прежнему существуют, но благодаря совершенствованию алгоритмов и эволюции аппаратных ускорителей производительность продолжает улучшаться темпами, превышающими закон Мура.&lt;/p>
&lt;p>Через несколько лет «вычисление зашифрованных данных» больше не будет чем-то особенным, а станет стандартной передовой практикой защиты данных в облачных сервисах. FHE — это основа безопасности следующего поколения, которая реализует &lt;strong>окончательную совместимость конфиденциальности и использования данных&lt;/strong> в обществе, управляемом данными.&lt;/p></description></item><item><title>Математические основы эллиптической криптографии (ECC) и её реализация на C++</title><link>http://kenji.blog/ru/p/elliptic-curve-cryptography-math-cpp/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/elliptic-curve-cryptography-math-cpp/</guid><description>&lt;img src="http://kenji.blog/p/elliptic-curve-cryptography-math-cpp/img/eyecatch.jpg" alt="Featured image of post Математические основы эллиптической криптографии (ECC) и её реализация на C++" />&lt;h1 id="математические-основы-эллиптической-криптографии-ecc-и-её-реализация-на-c">Математические основы эллиптической криптографии (ECC) и её реализация на C++
&lt;/h1>&lt;p>В современной криптографии &lt;strong>криптография на эллиптических кривых (Elliptic Curve Cryptography: ECC)&lt;/strong> играет исключительно важную роль. От наших повседневных интернет-коммуникаций (HTTPS/TLS) до безопасных анклавов смартфонов, аутентификации серверов через SSH, беспарольной аутентификации, такой как FIDO, и даже криптоактивов, таких как биткойн и Ethereum — не будет преувеличением сказать, что основа доверия в современном цифровом обществе держится на ECC.&lt;/p>
&lt;p>В этой статье мы подробно и объемно разберем, как работает криптография на эллиптических кривых. Мы начнем с прекрасной, но сложной математической теории, лежащей в её основе (алгебраическая геометрия над конечными полями), перейдем к практическим методам реализации на C++, а также рассмотрим методы безопасного написания кода для предотвращения атак по сторонним каналам (тайминг-атак).&lt;/p>
&lt;hr>
&lt;h2 id="1-почему-именно-криптография-на-эллиптических-кривых-сравнение-с-rsa">1. Почему именно криптография на эллиптических кривых? (Сравнение с RSA)
&lt;/h2>&lt;p>Долгое время синонимом криптографии с открытым ключом был алгоритм &lt;strong>RSA&lt;/strong>. Безопасность RSA основана на «сложности факторизации огромных составных чисел». Однако по мере роста вычислительных мощностей компьютеров, для поддержания уровня безопасности возникла необходимость постоянно увеличивать длину ключа RSA (количество бит модуля). В настоящее время рекомендуется использовать длину ключа как минимум 2048 бит, а для большей безопасности — 3072 или 4096 бит.&lt;/p>
&lt;p>С другой стороны, безопасность криптографии на эллиптических кривых (ECC) основана на совершенно иной математической сложности — &lt;strong>«задаче дискретного логарифмирования на эллиптической кривой (ECDLP)»&lt;/strong>. До сих пор не найдено эффективного алгоритма (например, субэкспоненциального алгоритма) для решения ECDLP, и даже самые эффективные известные методы атаки требуют экспоненциального времени.&lt;/p>
&lt;p>Благодаря этому свойству, решающим преимуществом ECC является то, что она &lt;strong>может обеспечить уровень безопасности, эквивалентный RSA, при значительно меньшей длине ключа&lt;/strong>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:center">Уровень безопасности (бит)&lt;/th>
&lt;th style="text-align:center">Длина ключа RSA (бит)&lt;/th>
&lt;th style="text-align:center">Длина ключа ECC (бит)&lt;/th>
&lt;th style="text-align:center">Соотношение длин ключей&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:center">80&lt;/td>
&lt;td style="text-align:center">1024&lt;/td>
&lt;td style="text-align:center">160&lt;/td>
&lt;td style="text-align:center">1:6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">112&lt;/td>
&lt;td style="text-align:center">2048&lt;/td>
&lt;td style="text-align:center">224&lt;/td>
&lt;td style="text-align:center">1:9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">128&lt;/td>
&lt;td style="text-align:center">3072&lt;/td>
&lt;td style="text-align:center">256&lt;/td>
&lt;td style="text-align:center">1:12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">192&lt;/td>
&lt;td style="text-align:center">7680&lt;/td>
&lt;td style="text-align:center">384&lt;/td>
&lt;td style="text-align:center">1:20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:center">256&lt;/td>
&lt;td style="text-align:center">15360&lt;/td>
&lt;td style="text-align:center">512&lt;/td>
&lt;td style="text-align:center">1:30&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Как показывает таблица выше, для достижения уровня безопасности в 128 бит (стандартный уровень на сегодняшний день) алгоритму RSA требуется ключ в 3072 бита, в то время как ECC достаточно всего 256 бит. Это позволяет сократить объем вычислений, уменьшить использование памяти и сэкономить пропускную способность сети, что дает подавляющее преимущество, особенно в средах с ограниченными ресурсами, таких как устройства IoT и смарт-карты.&lt;/p>
&lt;hr>
&lt;h2 id="2-математическая-подготовка-теория-групп-и-конечные-поля">2. Математическая подготовка: Теория групп и конечные поля
&lt;/h2>&lt;p>Чтобы по-настоящему понять криптографию на эллиптических кривых, необходимо усвоить базовые концепции абстрактной алгебры (теории групп и теории полей). Здесь мы кратко резюмируем базовые знания, необходимые для построения ECC.&lt;/p>
&lt;h3 id="21-группа-group-и-абелева-группа">2.1. Группа (Group) и Абелева группа
&lt;/h3>&lt;p>&lt;strong>Группа (Group)&lt;/strong> — это множество $G$ в паре с бинарной операцией (здесь мы будем использовать сложение $+$) на этом множестве, то есть пара $(G, +)$, которая удовлетворяет следующим четырем аксиомам:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Замкнутость (Closure)&lt;/strong>: Для любых $a, b \in G$ выполняется $a + b \in G$.&lt;/li>
&lt;li>&lt;strong>Ассоциативность (Associativity)&lt;/strong>: Для любых $a, b, c \in G$ выполняется $(a + b) + c = a + (b + c)$.&lt;/li>
&lt;li>&lt;strong>Существование нейтрального элемента (Identity element)&lt;/strong>: Существует такой элемент $e \in G$, что для любого $a \in G$ выполняется $a + e = e + a = a$. В случае аддитивной группы этот нейтральный элемент обычно обозначается как $0$ или $\mathcal{O}$.&lt;/li>
&lt;li>&lt;strong>Существование обратного элемента (Inverse element)&lt;/strong>: Для любого $a \in G$ существует элемент $b \in G$, такой что $a + b = b + a = e$. Этот элемент $b$ обозначается как $-a$.&lt;/li>
&lt;/ol>
&lt;p>Кроме того, если порядок выполнения операции не влияет на результат, то есть выполняется следующее условие, группа называется &lt;strong>абелевой (коммутативной) группой&lt;/strong>.&lt;/p>
&lt;ol start="5">
&lt;li>&lt;strong>Коммутативность (Commutativity)&lt;/strong>: Для любых $a, b \in G$ выполняется $a + b = b + a$.&lt;/li>
&lt;/ol>
&lt;p>Множество точек на эллиптической кривой образует эту &lt;strong>абелеву группу&lt;/strong>, если задать определенное правило сложения.&lt;/p>
&lt;h3 id="22-конечное-поле-finite-field">2.2. Конечное поле (Finite Field)
&lt;/h3>&lt;p>В криптографии используются не поля с непрерывным и бесконечным числом элементов, такие как действительные или комплексные числа, а &lt;strong>конечные поля (Finite Fields)&lt;/strong> или поля Галуа (Galois Fields), количество элементов в которых конечно.&lt;/p>
&lt;p>Самым базовым конечным полем является &lt;strong>простое поле $\mathbb{F}_p$&lt;/strong>, использующее простое число $p$. Оно представляет собой множество целых чисел $\{0, 1, 2, \dots, p-1\}$ с определенными на нем четырьмя арифметическими операциями (сложение, вычитание, умножение, деление) по модулю $p$ (остаток от деления на $p$).&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Сложение&lt;/strong>: $(a + b) \pmod p$&lt;/li>
&lt;li>&lt;strong>Вычитание&lt;/strong>: $(a - b) \pmod p$&lt;/li>
&lt;li>&lt;strong>Умножение&lt;/strong>: $(a \times b) \pmod p$&lt;/li>
&lt;li>&lt;strong>Деление&lt;/strong>: $a \times b^{-1} \pmod p$ (где $b^{-1}$ — мультипликативный обратный элемент к $b$ по модулю $p$)&lt;/li>
&lt;/ul>
&lt;p>Вычисление &lt;strong>мультипликативного обратного элемента (Modular Multiplicative Inverse)&lt;/strong> имеет решающее значение при реализации криптографии. Для нахождения $b^{-1}$, удовлетворяющего условию $b \times b^{-1} \equiv 1 \pmod p$, в основном используются следующие два алгоритма:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Расширенный алгоритм Евклида (Extended Euclidean Algorithm)&lt;/strong>: Он быстр, но в зависимости от реализации время обработки может зависеть от входных значений, что создает риск тайминг-атак.&lt;/li>
&lt;li>&lt;strong>Малая теорема Ферма (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>: Если $p$ — простое число и $b \neq 0$, то справедливо $b^{p-1} \equiv 1 \pmod p$. Разделив обе части на $b$, получим $b^{p-2} \equiv b^{-1} \pmod p$. То есть, возведя $b$ в степень $p-2$, можно найти обратный элемент. Поскольку операцию возведения в степень легко реализовать за постоянное время, в криптографии предпочитают именно этот метод.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-уравнение-эллиптической-кривой-и-геометрия">3. Уравнение эллиптической кривой и геометрия
&lt;/h2>&lt;h3 id="31-нормальная-форма-вейерштрасса">3.1. Нормальная форма Вейерштрасса
&lt;/h3>&lt;p>&lt;strong>Эллиптическая кривая (Elliptic Curve)&lt;/strong> — это плоская кривая, обычно определяемая уравнением, известным как &lt;strong>нормальная форма Вейерштрасса (Weierstrass normal form)&lt;/strong>:&lt;/p>
$$ y^2 = x^3 + ax + b $$
&lt;p>Здесь $a$ и $b$ — константы. Условием того, что кривая не имеет особых точек (самопересечений или точек возврата) — то есть является гладкой кривой — является то, что следующий &lt;strong>дискриминант (Discriminant) $\Delta$&lt;/strong> не равен нулю:&lt;/p>
$$ \Delta = -16(4a^3 + 27b^2) \neq 0 $$
&lt;p>Поскольку кривые с особыми точек снижают криптографическую безопасность, коэффициенты $a$ и $b$ всегда выбираются так, чтобы удовлетворять этому условию.&lt;/p>
&lt;h3 id="32-бесконечно-удаленная-точка-point-at-infinity">3.2. Бесконечно удаленная точка (Point at Infinity)
&lt;/h3>&lt;p>Для того чтобы эллиптическая кривая математически образовывала полную группу, в дополнение к точкам на плоскости вводится виртуальная точка, называемая &lt;strong>«бесконечно удаленной точкой (Point at Infinity)»&lt;/strong>. Она обозначается как $\mathcal{O}$.&lt;/p>
&lt;p>Бесконечно удаленная точка $\mathcal{O}$ определяется как точка, в которой пересекаются все вертикальные линии на бесконечности. В теории групп эта бесконечно удаленная точка $\mathcal{O}$ выступает в качестве &lt;strong>нейтрального элемента при сложении&lt;/strong> (нуля).&lt;/p>
&lt;p>То есть для любой точки $P$ на кривой справедливо следующее:
&lt;/p>
$$ P + \mathcal{O} = \mathcal{O} + P = P $$
&lt;p>Кроме того, обратный элемент $-P$ для точки $P = (x, y)$ определяется как точка, симметричная относительно оси x: $(x, -y)$. Таким образом:
&lt;/p>
$$ P + (-P) = \mathcal{O} $$
&lt;hr>
&lt;h2 id="4-групповые-операции-на-эллиптической-кривой-сложение-точек-и-удвоение">4. Групповые операции на эллиптической кривой (сложение точек и удвоение)
&lt;/h2>&lt;p>Основой криптографии на эллиптических кривых является операция &lt;strong>«сложения (Addition)»&lt;/strong> точек на кривой. Она отличается от обычного сложения целых чисел и определяется на основе геометрических операций.&lt;/p>
&lt;h3 id="41-геометрическое-сложение-метод-касательных-и-секущих">4.1. Геометрическое сложение (Метод касательных и секущих)
&lt;/h3>&lt;p>Процедура нахождения новой точки $R$ ($R = P + Q$) путем сложения двух разных точек $P$ и $Q$ на кривой выглядит следующим образом:&lt;/p>
&lt;ol>
&lt;li>Проведите прямую линию (секущую) через точки $P$ и $Q$.&lt;/li>
&lt;li>Эта линия обязательно пересечет эллиптическую кривую в третьей точке (назовем её $-R$). (※Согласно теореме алгебраической геометрии)&lt;/li>
&lt;li>Отразив точку пересечения $-R$ симметрично относительно оси x (инвертировав знак координаты y), мы получим искомую точку $R$.&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph TD
Step1["Провести прямую через P(x1, y1) и Q(x2, y2)"] --> Step2["Найти третью точку пересечения с кривой -R"]
Step2 --> Step3["Отразить -R относительно оси x, чтобы получить R(x3, y3)"]
Step3 -.-> Result["Это и есть R = P + Q"]&lt;/div>
&lt;h3 id="42-удвоение-точки-point-doubling">4.2. Удвоение точки (Point Doubling)
&lt;/h3>&lt;p>Если прибавить точку $P$ к самой себе ($P + P = 2P$), провести прямую через две точки невозможно. В этом случае проводится &lt;strong>касательная (Tangent) к кривой в точке $P$&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>Проведите касательную к кривой в точке $P$.&lt;/li>
&lt;li>Эта касательная пересечет кривую в еще одной точке $-R$.&lt;/li>
&lt;li>Отразив точку пересечения симметрично относительно оси x, мы получим искомую точку $R = 2P$.&lt;/li>
&lt;/ol>
&lt;h3 id="43-алгебраические-формулы-для-вычислений">4.3. Алгебраические формулы для вычислений
&lt;/h3>&lt;p>Геометрические операции можно преобразовать в алгебраические формулы для вычисления на компьютере.
Все операции выполняются &lt;strong>над конечным полем $\mathbb{F}_p$ (по модулю $p$)&lt;/strong>.&lt;/p>
&lt;p>Пусть точка $P = (x_1, y_1)$, а точка $Q = (x_2, y_2)$.
Также пусть результирующая точка $R = P + Q = (x_3, y_3)$.&lt;/p>
&lt;p>Обозначим наклон прямой через $\lambda$ (лямбда).&lt;/p>
&lt;p>&lt;strong>[Случай 1: $P \neq Q$ (Сложение точек)]&lt;/strong>
Наклон $\lambda$ — это скорость изменения между двумя точками.
&lt;/p>
$$ \lambda \equiv \frac{y_2 - y_1}{x_2 - x_1} \pmod p $$
$$ \lambda \equiv (y_2 - y_1) \cdot (x_2 - x_1)^{-1} \pmod p $$
&lt;p>Используя этот $\lambda$, можно найти $x_3$ и $y_3$ следующим образом:
&lt;/p>
$$ x_3 \equiv \lambda^2 - x_1 - x_2 \pmod p $$
$$ y_3 \equiv \lambda(x_1 - x_3) - y_1 \pmod p $$
&lt;p>&lt;strong>[Случай 2: $P = Q$ (Удвоение точки)]&lt;/strong>
Наклон $\lambda$ будет равен наклону касательной, который находится с помощью дифференцирования. (Неявно дифференцируем $y^2 = x^3 + ax + b$)
&lt;/p>
$$ 2y \cdot y' = 3x^2 + a \implies y' = \frac{3x^2 + a}{2y} $$
&lt;p>
Следовательно:
&lt;/p>
$$ \lambda \equiv (3x_1^2 + a) \cdot (2y_1)^{-1} \pmod p $$
&lt;p>Формулы для $x_3, y_3$ имеют ту же форму, что и при сложении, но поскольку $x_2 = x_1$, они выглядят так:
&lt;/p>
$$ x_3 \equiv \lambda^2 - 2x_1 \pmod p $$
$$ y_3 \equiv \lambda(x_1 - x_3) - y_1 \pmod p $$
&lt;blockquote>
&lt;p>[!IMPORTANT]
Эти формулы включают &lt;strong>деление (вычисление обратного элемента по модулю)&lt;/strong>, такое как $(x_2 - x_1)^{-1}$ или $(2y_1)^{-1}$. Поскольку вычисление обратного элемента по модулю требует больших вычислительных затрат, на практике обычно используются проективные системы координат, такие как &lt;strong>«координаты Якоби (Jacobian Coordinates)»&lt;/strong>, которые позволяют отложить выполнение деления.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="5-скалярное-умножение-и-задача-дискретного-логарифмирования-на-эллиптической-кривой-ecdlp">5. Скалярное умножение и задача дискретного логарифмирования на эллиптической кривой (ECDLP)
&lt;/h2>&lt;p>Операция, требующая наибольшего объема вычислений и являющаяся ядром безопасности в криптографии на эллиптических кривых, — это &lt;strong>скалярное умножение (Scalar Multiplication)&lt;/strong>.&lt;/p>
&lt;h3 id="51-что-такое-скалярное-умножение">5.1. Что такое скалярное умножение
&lt;/h3>&lt;p>Операция сложения точки $P$ с самой собой $k$ раз называется скалярным умножением и обозначается как $kP$.
&lt;/p>
$$ kP = \underbrace{P + P + \dots + P}_{k\text{ раз}} $$
&lt;p>Здесь $k$ — это очень большое целое число (например, 256-битное число).&lt;/p>
&lt;h3 id="52-задача-дискретного-логарифмирования-на-эллиптической-кривой-ecdlp">5.2. Задача дискретного логарифмирования на эллиптической кривой (ECDLP)
&lt;/h3>&lt;p>Безопасность криптографии на эллиптических кривых зависит от сложности следующей задачи.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Задача дискретного логарифмирования на эллиптической кривой (Elliptic Curve Discrete Logarithm Problem: ECDLP)&lt;/strong>
Даны известная точка $P$ (базовая точка) и результирующая точка $Q$. Найдите скаляр $k$, удовлетворяющий уравнению $Q = kP$.&lt;/p>
&lt;/blockquote>
&lt;p>Вычислить $Q$ из $k$ и $P$ (в прямом направлении) легко (выполняется за полиномиальное время) с использованием алгоритма, описанного ниже. Однако вычислить $k$ из $P$ и $Q$ в обратном направлении практически невозможно (односторонняя функция) без полного перебора, для чего не существует эффективных методов.
В криптографических протоколах &lt;strong>$k$ соответствует «закрытому ключу», а $Q$ — «открытому ключу»&lt;/strong>.&lt;/p>
&lt;h3 id="53-алгоритм-double-and-add-удвоение-и-сложение">5.3. Алгоритм Double-and-Add (Удвоение и сложение)
&lt;/h3>&lt;p>Если $k$ — огромное число (например, $2^{256}$), простое сложение точки $P$ $k$ раз не завершится даже за время жизни Вселенной. Поэтому для быстрого скалярного умножения используется &lt;strong>алгоритм Double-and-Add (бинарный метод)&lt;/strong>.&lt;/p>
&lt;p>Это эллиптический эквивалент метода возведения в степень (метода повторного возведения в квадрат) для целых чисел. Скаляр $k$ представляется в двоичном виде, и обработка идет от старшего бита к младшему.&lt;/p>
&lt;ol>
&lt;li>Инициализируем точку $R$, хранящую результат, значением $\mathcal{O}$.&lt;/li>
&lt;li>Для каждого бита $k$ от самого старшего до самого младшего повторяем следующее:
&lt;ul>
&lt;li>Удваиваем $R$ (Point Doubling: $R = 2R$)&lt;/li>
&lt;li>Если текущий бит равен &lt;code>1&lt;/code>, прибавляем $P$ к $R$ (Point Addition: $R = R + P$)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Благодаря этому алгоритму объем вычислений кардинально сокращается с $O(k)$ до $O(\log_2 k)$, и вычисление становится возможным за реальное время (в миллисекундах).&lt;/p>
&lt;hr>
&lt;h2 id="6-обмен-ключами-диффи-хеллмана-на-эллиптических-кривых-ecdh">6. Обмен ключами Диффи-Хеллмана на эллиптических кривых (ECDH)
&lt;/h2>&lt;p>Здесь мы объясним, как работает &lt;strong>протокол обмена ключами ECDH (Elliptic Curve Diffie-Hellman)&lt;/strong>, который является наиболее типичным примером применения ECC. ECDH — это механизм, позволяющий Алисе и Бобу безопасно сгенерировать и использовать общий секретный ключ (сеансовый ключ) в канале связи, который может прослушиваться (это ядро рукопожатия TLS).&lt;/p>
&lt;p>&lt;strong>[Предварительные параметры (Параметры домена)]&lt;/strong>
Обе стороны заранее договариваются о используемой эллиптической кривой $E$, простом числе $p$ и базовой точке $G$. (Например, NIST P-256 или secp256k1)&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Alice as "Alice (Алиса)"
participant Bob as "Bob (Боб)"
Note over Alice,Bob: "Открытые параметры: кривая E, базовая точка G, простое число p"
Alice->>Alice: "Генерация закрытого ключа d_A (случайное целое число)"
Alice->>Alice: "Вычисление открытого ключа Q_A = d_A * G"
Bob->>Bob: "Генерация закрытого ключа d_B (случайное целое число)"
Bob->>Bob: "Вычисление открытого ключа Q_B = d_B * G"
Alice->>Bob: "Отправка открытого ключа Q_A (открытый текст)"
Bob->>Alice: "Отправка открытого ключа Q_B (открытый текст)"
Alice->>Alice: "Вычисление общего секрета S = d_A * Q_B"
Bob->>Bob: "Вычисление общего секрета S = d_B * Q_A"
Note over Alice,Bob: "S = d_A * (d_B * G) = d_B * (d_A * G) = (d_A * d_B) * G"
Note over Alice,Bob: "Результаты вычислений S у обоих полностью совпадают!"&lt;/div>
&lt;p>Злоумышленник (Ева) может перехватить передаваемые по сети $G$, $Q_A$ и $Q_B$, но из-за сложности ECDLP он не сможет вычислить закрытый ключ Алисы $d_A$ из $Q_A = d_A \cdot G$. Кроме того, перемножение $Q_A$ и $Q_B$ не дает общий ключ $S$, поэтому злоумышленник не может вычислить $S$.&lt;/p>
&lt;hr>
&lt;h2 id="7-ловушки-при-реализации-атаки-по-сторонним-каналам-и-защита-от-них">7. Ловушки при реализации: Атаки по сторонним каналам и защита от них
&lt;/h2>&lt;p>Даже теоретически совершенный криптографический алгоритм может иметь уязвимости, возникающие в процессе его реализации в виде программы. Это и есть &lt;strong>«атака по сторонним каналам (Side-Channel Attack)»&lt;/strong>.&lt;/p>
&lt;h3 id="71-тайминг-атака-timing-attack">7.1. Тайминг-атака (Timing Attack)
&lt;/h3>&lt;p>Давайте вспомним описанный ранее алгоритм Double-and-Add.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Псевдокод уязвимого алгоритма Double-and-Add
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Infinity&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">255&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PointDoubling&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Выполняется всегда
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PointAddition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Выполняется только если бит равен 1!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Эта реализация имеет фатальный недостаток. Поскольку операция сложения точек (Point Addition) выполняется, когда бит равен &lt;code>1&lt;/code>, &lt;strong>время вычислений немного увеличивается&lt;/strong> по сравнению с ситуацией, когда бит равен &lt;code>0&lt;/code>. Кроме того, меняется поведение предсказателя переходов процессора и кэш-памяти.
Злоумышленник, статистически проанализировав эти крошечные различия во времени вычисления (или энергопотреблении) тысячи раз, может &lt;strong>полностью восстановить последовательность битов закрытого ключа $k$ по одному биту&lt;/strong>. Это называется тайминг-атакой.&lt;/p>
&lt;h3 id="72-реализация-с-постоянным-временем-constant-time-лестница-монтгомери-montgomery-ladder">7.2. Реализация с постоянным временем (Constant-Time): Лестница Монтгомери (Montgomery Ladder)
&lt;/h3>&lt;p>Чтобы предотвратить тайминг-атаки, необходимо использовать алгоритмы, в которых &lt;strong>последовательность выполняемых инструкций и время вычислений всегда остаются постоянными (Constant-Time) независимо от значения битов закрытого ключа&lt;/strong>.&lt;/p>
&lt;p>Типичным примером такого алгоритма является &lt;strong>Лестница Монтгомери (Montgomery Ladder)&lt;/strong>.&lt;/p>
&lt;div class="mermaid">graph TD
Start["Инициализация: R0 = O, R1 = P"] --> LoopStart["Для каждого бита i (начиная со старшего)"]
LoopStart --> Cond{"Каково значение k_i?"}
Cond -->|0| Branch0["R1 = R0 + R1&lt;br>R0 = 2 * R0"]
Cond -->|1| Branch1["R0 = R0 + R1&lt;br>R1 = 2 * R1"]
Branch0 --> LoopEnd["К следующему биту"]
Branch1 --> LoopEnd
LoopEnd --> LoopStart
LoopStart -.->|"Все биты обработаны"| End["Конец: R0 - это результат (kP)"]&lt;/div>
&lt;p>Прелесть лестницы Монтгомери заключается в том, что независимо от того, равен бит &lt;code>0&lt;/code> или &lt;code>1&lt;/code>, &lt;strong>«всегда выполняется одно сложение точек и одно удвоение точки»&lt;/strong>. Это полностью устраняет зависимость времени вычисления от данных.&lt;/p>
&lt;p>Однако если существует само ветвление (&lt;code>if (k_i == 0)&lt;/code>), остается риск того, что время выполнения будет варьироваться из-за оптимизаций компилятора или предсказания переходов ЦП. Поэтому в реальных Constant-Time реализациях устраняют условные переходы (операторы &lt;code>if&lt;/code>) и используют &lt;strong>условный обмен (Conditional Swap), основанный на побитовых операциях&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="8-реализация-криптографии-на-эллиптических-кривых-на-c">8. Реализация криптографии на эллиптических кривых на C++
&lt;/h2>&lt;p>Теперь переведем теорию в код на C++. Практические криптографические библиотеки (такие как OpenSSL или libsodium) используют сложные ассемблерные оптимизации и координаты Якоби, но здесь мы покажем структуру &lt;strong>понятной Constant-Time реализации с использованием аффинных координат&lt;/strong> для лучшего понимания математики.&lt;/p>
&lt;p>Предположим, что для работы с большими целыми числами используется &lt;code>boost::multiprecision::cpp_int&lt;/code>.&lt;/p>
&lt;h3 id="81-модульная-арифметика-и-обратный-элемент">8.1. Модульная арифметика и обратный элемент
&lt;/h3>&lt;p>Сначала определим вспомогательные функции для операций в конечном поле. Мы реализуем вычисление обратного элемента с использованием Малой теоремы Ферма.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;boost/multiprecision/cpp_int.hpp&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">boost&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">multiprecision&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Пример простого числа p и параметров для secp256k1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Операция по модулю, возвращающая положительный остаток
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nl">m&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Вычисление степени по модулю (x^y mod m)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">powerMod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exp&lt;/span> &lt;span class="o">/=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Обратный элемент по модулю с использованием малой теоремы Ферма
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="nf">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Предполагается, что m - простое число: n^(m-2) ≡ n^(-1) mod m
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">powerMod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="82-представление-точек-и-групповые-операции-сложение-удвоение">8.2. Представление точек и групповые операции (сложение, удвоение)
&lt;/h3>&lt;p>Определим структуру &lt;code>Point&lt;/code>, где бесконечно удаленная точка управляется с помощью флага, и реализуем формулы сложения.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Point&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Создание бесконечно удаленной точки
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">true&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Создание обычной точки
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">isInfinity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Сложение точек на эллиптической кривой (R = P + Q)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="nf">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isInfinity&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isInfinity&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// P + (-P) = бесконечно удаленная точка
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">lambda&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Удвоение точки (Point Doubling) (Если P = Q)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// lambda = (3x^2 + a) / 2y
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">den&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lambda&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">den&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Сложение точек (Point Addition) (Если P != Q)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// lambda = (y2 - y1) / (x2 - x1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">den&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">modInverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lambda&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">den&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">x3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lambda&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">lambda&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">y3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lambda&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">x3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="83-реализация-условного-обмена-с-постоянным-временем-constant-time-conditional-swap">8.3. Реализация условного обмена с постоянным временем (Constant-Time Conditional Swap)
&lt;/h3>&lt;p>При обмене содержимым переменных на основе битового значения секретного ключа, мы выполняем обмен, используя только побитовые операции (маскирование) без операторов &lt;code>if&lt;/code>. Это обеспечивает неизменность пути выполнения.&lt;/p>
&lt;blockquote>
&lt;p>[!TIP]
На практике классы для работы с длинной арифметикой, динамически выделяющие память, такие как &lt;code>cpp_int&lt;/code>, не подходят для операций с постоянным временем. Это связано с тем, что информация о тайминге утекает через выделение памяти и изменение размеров массивов. Практические библиотеки используют представления фиксированной длины (например, массив из 4 элементов uint64_t) и реализуют операции маскирования на битовом уровне. Ниже приведен концептуальный пример.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Концептуальный обмен с постоянным временем (предполагается целое число фиксированной длины)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Если bit равен 1, то меняет P1 и P2 местами, если 0 - не меняет
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="nf">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// bit равен 0 или 1. Если bit=1, маска состоит из всех единиц (0xFF..), если 0 - из всех нулей.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// (Здесь для пояснения предполагается, что каждое слово класса BigInt фиксированной длины - это w)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint64_t mask = 0 - (uint64_t)bit;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> for (int i = 0; i &amp;lt; NUM_WORDS; i++) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint64_t dummy = mask &amp;amp; (P1.x.words[i] ^ P2.x.words[i]);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> P1.x.words[i] ^= dummy;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> P2.x.words[i] ^= dummy;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> // Координата y и флаг isInfinity обрабатываются аналогично
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ※ Полный swap за постоянное время в boost::multiprecision затруднителен,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// но здесь мы ограничиваемся симуляцией с использованием ветвления для понимания логики.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bit&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">P1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">P2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="84-скалярное-умножение-с-использованием-лестницы-монтгомери">8.4. Скалярное умножение с использованием лестницы Монтгомери
&lt;/h3>&lt;p>Объединив ранее описанные &lt;code>pointAdd&lt;/code> и &lt;code>cswap&lt;/code>, реализуем безопасное скалярное умножение.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Скалярное умножение k * P (Метод лестницы Монтгомери)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Point&lt;/span> &lt;span class="nf">scalarMultiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cpp_int&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Point&lt;/span> &lt;span class="n">R0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Point&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// Бесконечно удаленная точка
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Point&lt;/span> &lt;span class="n">R1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">P&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Получение длины k в битах (256 бит для secp256k1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">numBits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">256&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">numBits&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Получение значения i-го бита (0 или 1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">bit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bit_test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Если bit == 1, меняем местами R0 и R1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Всегда выполняются одни и те же операции (Сложение точек и Удвоение точки)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">R1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">R0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pointAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Если bit == 1, меняем местами снова, чтобы восстановить состояние
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cswap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">R1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">R0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Благодаря этой логике реализации, независимо от того, равен ли каждый бит скаляра $k$ &lt;code>0&lt;/code> или &lt;code>1&lt;/code>, операции, выполняемые в каждой итерации цикла (&lt;code>cswap&lt;/code> $\to$ &lt;code>pointAdd&lt;/code> $\to$ &lt;code>pointAdd&lt;/code> $\to$ &lt;code>cswap&lt;/code>), имеют абсолютно идентичный поток выполнения. Это обеспечивает надежную защиту от утечек секретной информации через различия во времени или шаблоны доступа к кэшу.&lt;/p>
&lt;hr>
&lt;h2 id="9-заключение">9. Заключение
&lt;/h2>&lt;p>Криптография на эллиптических кривых (ECC) на первый взгляд может показаться загадкой: «Каким образом геометрическая операция проведения прямой линии и отражения точки пересечения становится криптографией?» Однако, отобразив это в дискретный мир конечных полей, можно построить превосходную одностороннюю функцию (задачу дискретного логарифмирования), что является плодом чудесного слияния математики и криптографии.&lt;/p>
&lt;p>В этой статье мы объяснили следующие важные моменты:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Преимущество перед RSA&lt;/strong>: Обеспечивает высокий уровень безопасности при очень малой длине ключа, что идеально подходит для современной эры мобильных устройств и IoT.&lt;/li>
&lt;li>&lt;strong>Основы теории групп и конечных полей&lt;/strong>: Математическая структура, являющаяся фундаментом ECC.&lt;/li>
&lt;li>&lt;strong>Формулы сложения и удвоения&lt;/strong>: Метод реализации алгебраических групповых операций с использованием уравнения Вейерштрасса.&lt;/li>
&lt;li>&lt;strong>Угроза атак по сторонним каналам&lt;/strong>: То, что условные переходы, зависящие от битов секретного ключа, создают фатальные уязвимости.&lt;/li>
&lt;li>&lt;strong>Реализация с постоянным временем (Constant-Time)&lt;/strong>: Техника кодирования на C++ для унификации поведения на аппаратном уровне и предотвращения атак с использованием Лестницы Монтгомери (Montgomery Ladder) и Условного обмена (Conditional Swap).&lt;/li>
&lt;/ol>
&lt;p>Создание собственной криптографической библиотеки для работы в продакшен-среде настоятельно не рекомендуется («Don&amp;rsquo;t roll your own crypto») из-за чрезвычайно высоких рисков безопасности. Тем не менее, глубокое понимание алгоритмов и математической основы, стоящей за ними, безусловно, станет мощным оружием для инженеров, проектирующих и эксплуатирующих более безопасные и производительные системы.&lt;/p>
&lt;p>В следующей статье мы планируем глубже погрузиться в механизмы &lt;strong>алгоритма цифровой подписи на эллиптических кривых (ECDSA - Elliptic Curve Digital Signature Algorithm)&lt;/strong> и &lt;strong>подписей Шнорра (Schnorr)&lt;/strong>, которые используются в Биткойне.&lt;/p></description></item><item><title>Существуют ли алгоритмы, превосходящие GNFS (общий метод решета числового поля)?</title><link>http://kenji.blog/ru/p/beyond-gnfs-integer-factorization-algorithms/</link><pubDate>Fri, 11 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/beyond-gnfs-integer-factorization-algorithms/</guid><description>&lt;img src="http://kenji.blog/p/beyond-gnfs-integer-factorization-algorithms/img/eyecatch.jpg" alt="Featured image of post Существуют ли алгоритмы, превосходящие GNFS (общий метод решета числового поля)?" />&lt;h2 id="1-введение-факторизация-целых-чисел-и-основа-современной-криптографии">1. Введение: Факторизация целых чисел и основа современной криптографии
&lt;/h2>&lt;p>Безопасность интернет-коммуникаций в современном обществе во многом зависит от безопасности криптографии с открытым ключом, в частности алгоритма RSA. А безопасность RSA основана на математическом предположении о &amp;ldquo;сложности факторизации огромных составных чисел&amp;rdquo;. Если будет найден чрезвычайно эффективный алгоритм факторизации целых чисел, глобальная коммуникационная инфраструктура будет разрушена до основания.&lt;/p>
&lt;p>В настоящее время &lt;strong>общий метод решета числового поля (GNFS: General Number Field Sieve)&lt;/strong> является самым быстрым и мощным алгоритмом факторизации огромных целых чисел на классических компьютерах. GNFS появился как расширение специального метода решета числового поля (SNFS), предложенного в конце 1980-х годов, и до сих пор удерживает рекорды по факторизации таких огромных составных чисел, как RSA-768 и RSA-250.&lt;/p>
&lt;p>Однако криптографы и математики постоянно задаются следующими вопросами: &amp;ldquo;Существует ли классический алгоритм, превосходящий GNFS?&amp;rdquo;, &amp;ldquo;Где лежат границы возможностей классических компьютеров?&amp;rdquo; и &amp;ldquo;Как квантовые компьютеры смогут преодолеть этот барьер?&amp;rdquo;.&lt;/p>
&lt;p>В этой статье мы подробно разберем глубокую математическую структуру, лежащую в основе GNFS, и проведем детальный технический анализ выбора полиномов, процесса просеивания и этапа линейной алгебры с использованием метода Блока-Видемана. Кроме того, мы рассмотрим методы расширения GNFS, такие как улучшения Копперсмита, и сравним фундаментальные различия между классическими алгоритмами субэкспоненциального времени (Sub-exponential time) и квантовыми алгоритмами полиномиального времени с математической точки зрения.&lt;/p>
&lt;hr>
&lt;h2 id="2-асимптотическая-сложность-и-l-нотация-l-notation">2. Асимптотическая сложность и L-нотация (L-notation)
&lt;/h2>&lt;p>При оценке сложности алгоритмов факторизации целых чисел для выражения субэкспоненциального времени относительно количества цифр входного числа $n$ используется &lt;strong>L-нотация (L-notation)&lt;/strong>, а не стандартная запись полиномиального времени (например, $O(n^k)$). L-нотация определяется следующим образом:&lt;/p>
$$
L_n[\alpha, c] = \exp \left( (c + o(1)) (\ln n)^\alpha (\ln \ln n)^{1-\alpha} \right)
$$
&lt;p>Где $n$ — целое число, которое необходимо факторизовать, а $\ln n$ — натуральный логарифм, пропорциональный битовой длине $n$.&lt;/p>
&lt;ul>
&lt;li>Если $\alpha = 0$: $L_n[0, c] = \exp(c \ln \ln n) = (\ln n)^c$, что представляет &lt;strong>полиномиальное время (Polynomial time)&lt;/strong> относительно битовой длины.&lt;/li>
&lt;li>Если $\alpha = 1$: $L_n[1, c] = \exp(c \ln n) = n^c$, что представляет &lt;strong>экспоненциальное время (Exponential time)&lt;/strong> относительно битовой длины.&lt;/li>
&lt;li>Если $0 &lt; \alpha &lt; 1$: сложность находится между полиномиальным и экспоненциальным временем, образуя класс &lt;strong>субэкспоненциального времени (Sub-exponential time)&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>Эволюция алгоритмов факторизации в прошлом по сути была историей постепенного уменьшения этого значения $\alpha$.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Метод цепных дробей (CFRAC) и метод множественного полиномиального квадратичного решета (MPQS)&lt;/strong>: относятся к классу $\alpha = 1/2$, их сложность составляет примерно $L_n[1/2, 1]$.&lt;/li>
&lt;li>&lt;strong>Общий метод решета числового поля (GNFS)&lt;/strong>: достигает $\alpha = 1/3$ и обладает невероятной сложностью $L_n[1/3, (64/9)^{1/3}]$, являясь самым быстрым среди известных классических алгоритмов в настоящее время.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="3-общая-картина-алгоритма-и-математическая-структура-gnfs-общий-метод-решета-числового-поля">3. Общая картина алгоритма и математическая структура GNFS (Общий метод решета числового поля)
&lt;/h2>&lt;p>GNFS имеет очень сложную и глубокую математическую базу. Основная идея является продолжением Малой теоремы Ферма и метода квадратичного решета (QS): она заключается в нахождении нетривиальной пары $(X, Y)$, удовлетворяющей сравнению $X^2 \equiv Y^2 \pmod n$ и при этом $X \not\equiv \pm Y \pmod n$, что позволяет вывести множитель числа $n$ как $\gcd(X-Y, n)$.&lt;/p>
&lt;p>Однако суть GNFS состоит в том, что это делается не только в поле рациональных чисел $\mathbb{Q}$, но и путем одновременного поиска &amp;ldquo;гладких чисел (Smooth numbers)&amp;rdquo; как в поле расширения $\mathbb{Q}(\alpha)$, называемом алгебраическим числовым полем (Algebraic Number Field), так и в поле рациональных чисел, выстраивая отношения конгруэнтности через гомоморфизмы.&lt;/p>
&lt;p>Процесс GNFS в основном делится на 5 этапов.&lt;/p>
&lt;div class="mermaid">graph TD
A["Проблема факторизации (Вход n)"] --> B["1. Выбор полинома (Polynomial Selection)"]
B --> C["2. Просеивание (Sieving Phase)"]
C --> D["3. Фильтрация (Filtering Phase)"]
D --> E["4. Линейная алгебра (Linear Algebra Phase)"]
E --> F["5. Извлечение квадратного корня (Square Root Phase)"]
F --> G["Вывод простых множителей p, q"]&lt;/div>
&lt;h3 id="31-этап-1-выбор-полинома-polynomial-selection">3.1 Этап 1: Выбор полинома (Polynomial Selection)
&lt;/h3>&lt;p>Успех GNFS во многом зависит от выбора подходящих полиномов. Цель — найти два неприводимых полинома $f_1(x)$ (на рациональной стороне) и $f_2(x)$ (на алгебраической стороне), имеющих общий корень $m$. То есть, они должны удовлетворять условию:
$f_1(m) \equiv f_2(m) \equiv 0 \pmod n$&lt;/p>
&lt;p>Обычно для рациональной стороны выбирают линейный полином $f_1(x) = x - m$, а для алгебраической стороны $f_2(x)$ выбирают унитарный полином степени $d$ (чаще всего 5 или 6). Наиболее классическим подходом является &lt;strong>метод Base-$m$&lt;/strong>.
Выбирается целое число $m = \lfloor n^{1/(d+1)} \rfloor$, близкое к $1/(d+1)$-й степени $n$, и $n$ раскладывается по основанию $m$:
$n = c_d m^d + c_{d-1} m^{d-1} + \dots + c_1 m + c_0$
Таким образом мы получаем полином $f_2(x) = c_d x^d + c_{d-1} x^{d-1} + \dots + c_0$. Очевидно, что $f_2(m) = n \equiv 0 \pmod n$.&lt;/p>
&lt;p>Однако в современных реализациях используется &lt;strong>алгоритм Кляйньюнга (Kleinjung&amp;rsquo;s algorithm)&lt;/strong>. Он находит полиномы, оптимизирующие алгебраические свойства (значение Murphy&amp;rsquo;s $E$ или $\alpha$-value) и увеличивающие вероятность генерации гладких чисел на этапе просеивания, при этом не допуская чрезмерного увеличения коэффициентов полинома (оптимизация Skewness). В один только этот шаг вкладываются огромные вычислительные ресурсы.&lt;/p>
&lt;h3 id="32-этап-2-просеивание-sieving-phase">3.2 Этап 2: Просеивание (Sieving Phase)
&lt;/h3>&lt;p>После того как полиномы определены, алгоритм переходит к этапу &amp;ldquo;просеивания (Sieving)&amp;rdquo;, который является самым вычислительно ресурсоемким. Здесь осуществляется поиск пар $(a, b)$. Эта пара взаимно проста, и требуется, чтобы следующие два значения одновременно были &amp;ldquo;гладкими (Smooth)&amp;rdquo;:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Норма на рациональной стороне&lt;/strong>: $F_1(a, b) = b \cdot f_1(a/b) = a - bm$&lt;/li>
&lt;li>&lt;strong>Норма на алгебраической стороне&lt;/strong>: $F_2(a, b) = b^d \cdot f_2(a/b)$&lt;/li>
&lt;/ol>
&lt;p>&amp;ldquo;Гладкий&amp;rdquo; означает, что число можно факторизовать, используя только простые числа, не превышающие заданного предела (Sieve bound). Подготавливаются базы простых чисел (Factor base) для рациональной и алгебраической сторон, и гладкие числа эффективно находятся в огромном пространстве поиска по принципу решета Эратосфена.
В настоящее время основным методом является так называемое &lt;strong>просеивание по решетке (Lattice Sieving)&lt;/strong>, при котором фиксируется определенное простое число $q$, и просеиваются только те пары $(a, b)$ на подрешетке, для которых и рациональная, и алгебраическая стороны кратны $q$, что позволяет достичь крайне высокой эффективности.&lt;/p>
&lt;h3 id="33-этап-3-фильтрация-filtering-phase">3.3 Этап 3: Фильтрация (Filtering Phase)
&lt;/h3>&lt;p>Количество гладких соотношений (relations), найденных в процессе просеивания, достигает сотен миллионов или миллиардов. Однако многие из них содержат бесполезную информацию.
Цель фильтрации — построить гигантскую разреженную матрицу (Sparse Matrix), одновременно минимизировав её размерность.&lt;/p>
&lt;p>В частности, выполняются следующие операции:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Singleton removal&lt;/strong>: Удаление соотношений, содержащих простые множители, которые встречаются только один раз.&lt;/li>
&lt;li>&lt;strong>Clique removal / Merging&lt;/strong>: Перемножение соотношений, имеющих общие простые множители (встречающиеся 2 и более раз), для исключения переменных и сокращения в систему уравнений с более высокой плотностью, но меньшей размерности.&lt;/li>
&lt;/ul>
&lt;p>Благодаря этому матрица из миллиардов строк сжимается до гигантской разреженной матрицы $\mathbf{A}$ (элементы которой — 0 и 1 над полем $\mathbb{F}_2$) размером в десятки миллионов строк.&lt;/p>
&lt;h3 id="34-этап-4-линейная-алгебра-linear-algebra-phase">3.4 Этап 4: Линейная алгебра (Linear Algebra Phase)
&lt;/h3>&lt;p>Здесь мы находим нетривиальный вектор решений $\mathbf{x}$ уравнения $\mathbf{A} \mathbf{x} \equiv \mathbf{0} \pmod 2$. Иными словами, это задача нахождения левого нуль-пространства (Left Nullspace) гигантской разреженной матрицы.&lt;/p>
&lt;p>Поскольку размер матрицы экстремально велик, вычисления обычным методом Гаусса ($O(N^3)$) абсолютно невозможны. Поэтому используются итерационные методы, относящиеся к методам подпространства Крылова. Исторически использовался &lt;strong>блочный метод Ланцоша (Block Lanczos)&lt;/strong>, но в современных распределенных вычислительных средах доминирует &lt;strong>блочный метод Видемана (Block Wiedemann Algorithm)&lt;/strong>, способный радикально снизить накладные расходы на обмен данными.&lt;/p>
&lt;p>Блочный метод Видемана вычисляет минимальный полином на основе матрицы $\mathbf{A}$ и последовательности векторов, а затем использует алгоритм Берлекэмпа-Мэсси (Berlekamp-Massey) для построения базиса нуль-пространства. Этот шаг крайне трудно поддается распараллеливанию и требует наличия суперкомпьютеров или тесно связанных коммуникационных сетей крупномасштабных кластеров, что является одним из главных узких мест GNFS.&lt;/p>
&lt;h3 id="35-этап-5-извлечение-квадратного-корня-square-root-phase">3.5 Этап 5: Извлечение квадратного корня (Square Root Phase)
&lt;/h3>&lt;p>Из решений линейной алгебры конструируются произведения, которые образуют &amp;ldquo;полные квадраты&amp;rdquo; на рациональной и алгебраической сторонах.
На рациональной стороне $\prod (a-bm)$ становится квадратом $X^2$ некоего целого числа $X$, а на алгебраической стороне произведение соответствующих идеалов образует полный квадрат $\gamma^2$ над алгебраическим полем.
Вычисляя этот $\gamma$ в алгебраическом поле и применяя гомоморфизм $\phi: \alpha \mapsto m \pmod n$ в кольцо рациональных целых чисел, получаем сравнение:
$X^2 \equiv \phi(\gamma)^2 \equiv Y^2 \pmod n$&lt;/p>
&lt;p>Для изчисления квадратного корня над алгебраическим полем используются сложные алгоритмы, такие как &lt;strong>метод Монтгомери (Montgomery&amp;rsquo;s Method)&lt;/strong>, требующие глубоких знаний алгебраической теории чисел. В итоге вычисляется $\gcd(X-Y, n)$, и если найден нетривиальный множитель, факторизация завершена.&lt;/p>
&lt;hr>
&lt;h2 id="4-существуют-ли-классические-алгоритмы-превосходящие-gnfs">4. Существуют ли классические алгоритмы, превосходящие GNFS?
&lt;/h2>&lt;p>На сегодняшний день не обнаружено классического алгоритма факторизации произвольных целых чисел, асимптотическая сложность которого была бы ниже $L_n[1/3, c]$. Тем не менее, существуют некоторые попытки и производные алгоритмы, стремящиеся преодолеть теоретические и практические пределы.&lt;/p>
&lt;h3 id="41-метод-решета-множественного-числового-поля-mnfs-multiple-number-field-sieve">4.1 Метод решета множественного числового поля (MNFS: Multiple Number Field Sieve)
&lt;/h3>&lt;p>В качестве расширения GNFS существует &lt;strong>метод решета множественного числового поля (MNFS)&lt;/strong>, предложенный Д. Копперсмитом. В то время как GNFS использует 2 полинома (рациональный и алгебраический), MNFS одновременно применяет несколько различных алгебраических полиномов для одного рационального полинома:&lt;/p>
$$ f_1(x), f_{2,1}(x), f_{2,2}(x), \dots, f_{2,V}(x) $$
&lt;p>Использование нескольких алгебраических полей позволяет резко повысить вероятность того, что число окажется &amp;ldquo;гладким в каком-либо из алгебраических полей&amp;rdquo; на каждом этапе просеивания. Благодаря этому подходу Копперсмиту удалось незначительно уменьшить константу $c$ в сложности $L_n[1/3, c]$.
В частности, теоретически доказано, что константа GNFS $c = (64/9)^{1/3} \approx 1.923$ при оптимизации с помощью MNFS может быть снижена примерно до $c \approx 1.902$.
Однако на практике накладные расходы на управление множественными полями огромны, и это не привело к решающему прорыву для модулей RSA практических размеров.&lt;/p>
&lt;h3 id="42-возможны-ли-алгоритмы-класса-l_n14">4.2 Возможны ли алгоритмы класса $L_n[1/4]$?
&lt;/h3>&lt;p>Тема &amp;ldquo;Существуют ли алгоритмы с показателем $\alpha = 1/4$?&amp;rdquo; на протяжении многих лет остается предметом дебатов среди математиков, обсуждающих пределы классических алгоритмов факторизации.
Текущий GNFS и его производные сильно привязаны к концепции &amp;ldquo;поиска гладкости&amp;rdquo; с помощью решета, и в рамках этой парадигмы широко распространено мнение, что $\alpha = 1/3$ является пределом. Анализ вероятности распределения гладких целых чисел с использованием функции Дикмана (Dickman function) также показывает, что при нынешних методах построения алгебраических полей в сочетании с решетом невозможно преодолеть барьер $O(L_n[1/3])$, как бы ни проводилась оптимизация.&lt;/p>
&lt;p>Если алгоритм $L_n[1/4]$ или же классический алгоритм полиномиального времени действительно существует, он должен опираться на совершенно иную математическую структуру, не основанную на &amp;ldquo;поиске гладкости&amp;rdquo;, как GNFS (например, на более продвинутые подходы из алгебраической геометрии, подобные алгоритму Схофа (Schoof&amp;rsquo;s algorithm) для эллиптической криптографии), которую человечество пока не может себе представить. Однако в настоящее время предпосылок для этого не наблюдается.&lt;/p>
&lt;hr>
&lt;h2 id="5-прорыв-квантовых-компьютеров-алгоритм-шора">5. Прорыв квантовых компьютеров: алгоритм Шора
&lt;/h2>&lt;p>В то время как классические компьютеры столкнулись с барьером $L_n[1/3]$, этот барьер был разрушен &lt;strong>алгоритмом Шора (Shor&amp;rsquo;s Algorithm)&lt;/strong>, опубликованным Питером Шором в 1994 году, который в корне изменил саму модель вычислений.&lt;/p>
&lt;h3 id="51-шок-квантового-полиномиального-времени">5.1 Шок квантового полиномиального времени
&lt;/h3>&lt;p>Алгоритм Шора сводит задачу факторизации к &amp;ldquo;задаче нахождения порядка (Order Finding Problem)&amp;rdquo;. Для заданного целого числа $a$ это задача нахождения периода (порядка) $r$ функции $f(x) = a^x \pmod n$.
Классическому компьютеру требуется экспоненциальное время, чтобы найти этот период, но, используя &lt;strong>квантовую оценку фазы (QPE: Quantum Phase Estimation)&lt;/strong> и &lt;strong>квантовое преобразование Фурье (QFT: Quantum Fourier Transform)&lt;/strong> на квантовом компьютере, можно параллельно оценить суперпозицию всех состояний и с высокой вероятностью извлечь период $r$.&lt;/p>
&lt;p>С точки зрения сложности, время выполнения алгоритма Шора относится к &lt;strong>квантовому полиномиальному времени&lt;/strong>, а именно:
&lt;/p>
$$ O((\log n)^3) $$
&lt;p>
С учетом последних оптимизаций схем считается, что эту величину можно сократить до $O((\log n)^2 \log \log n)$.&lt;/p>
&lt;div class="mermaid">graph LR
A["Классический алгоритм (GNFS)"] -->|Предел| B["Субэкспоненциальное время L_n[1/3]"]
C["Квантовый алгоритм (Shor)"] -->|Прорыв| D["Полиномиальное время O((log n)^3)"]
B --> E["Продолжение использования RSA (Увеличение длины ключа)"]
D --> F["Полное разрушение RSA"]&lt;/div>
&lt;h3 id="52-классическое-субэкспоненциальное-время-против-квантового-полиномиального-времени">5.2 Классическое субэкспоненциальное время против квантового полиномиального времени
&lt;/h3>&lt;p>Разница между этими двумя классами сложности имеет решающее значение для безопасности криптографии в реальном мире.&lt;/p>
&lt;p>Например, рассмотрим случай факторизации RSA-2048 (составное число из 2048 бит).&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GNFS (классический)&lt;/strong>: при подстановке $n \approx 2^{2048}$ в $L_n[1/3, 1.923]$ потребуется примерно $2^{112}$ операций. Это астрономический объем вычислений, который займет время, превышающее возраст Вселенной, даже если объединить все вычислительные ресурсы, существующие сейчас на Земле.&lt;/li>
&lt;li>&lt;strong>Алгоритм Шора (квантовый)&lt;/strong>: алгоритму $O((\log n)^3)$ потребуется около $2048^3 \approx 8.5 \times 10^9$ операций с логическими вентилями. Это означает, что при наличии подходящего оборудования (универсального квантового компьютера с миллионами физических кубитов и возможностью исправления ошибок) вычисления могут быть завершены всего за несколько часов или дней.&lt;/li>
&lt;/ul>
&lt;p>Смена парадигмы с субэкспоненциальной сложности с &amp;ldquo;показателем $\alpha=1/3$&amp;rdquo; на &amp;ldquo;полиномиальное время&amp;rdquo; делает традиционную криптографическую стратегию обеспечения безопасности за счет увеличения длины ключа неэффективной.&lt;/p>
&lt;hr>
&lt;h2 id="6-заключение-перспективы-для-следующего-поколения">6. Заключение: Перспективы для следующего поколения
&lt;/h2>&lt;p>Текущий консенсус в научном сообществе по вопросу &amp;ldquo;Существуют ли классические алгоритмы, превосходящие GNFS?&amp;rdquo; таков:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Практические улучшения продолжаются, но асимптотических скачков не предвидится&lt;/strong>: Попытки улучшить константу $c$ в GNFS продолжаются, включая MNFS, оптимизацию выбора полиномов и распараллеливание метода Блока-Видемана. Однако вероятность открытия классического алгоритма со сложностью ниже $\alpha = 1/3$ считается крайне низкой.&lt;/li>
&lt;li>&lt;strong>Безопасность RSA на классических компьютерах остается надежной&lt;/strong>: Вычислительная сложность GNFS по-прежнему огромна, и RSA-2048 или RSA-4096 будут оставаться безопасными от атак на классических компьютерах в течение многих последующих десятилетий.&lt;/li>
&lt;li>&lt;strong>Истинная угроза исходит от квантовых алгоритмов&lt;/strong>: Барьер вычислительной сложности был преодолен алгоритмом Шора, основанным на принципах квантовой механики. Из-за этого мир вынужден переходить на постквантовую криптографию (PQC: Post-Quantum Cryptography). Переход к новым математическим проблемам, которые считаются сложными для решения (не могут быть решены за полиномиальное время) даже на квантовых компьютерах, таким как криптография на решетках и криптография на основе хешей, сегодня является передним краем криптографии.&lt;/li>
&lt;/ol>
&lt;p>Общий метод решета числового поля (GNFS) — это одна из &amp;ldquo;высших точек&amp;rdquo;, достигнутых человечеством в своем стремлении дойти до пределов классической математики и проектирования алгоритмов. Понимание глубокой математической структуры GNFS — это не просто изучение истории криптоанализа, но и путешествие в мир интеллектуальных исследований, позволяющее прикоснуться к красоте теории вычислительной сложности и алгебраической теории чисел. До того дня, когда квантовые компьютеры найдут практическое применение, GNFS, вероятно, будет продолжать удерживать трон как самый мощный алгоритм факторизации простых чисел.&lt;/p></description></item><item><title>【Иллюстрированное руководство по PQC】Сравнение основных алгоритмов постквантовой криптографии</title><link>http://kenji.blog/ru/p/post-quantum-cryptography-algorithms-comparison/</link><pubDate>Fri, 11 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/post-quantum-cryptography-algorithms-comparison/</guid><description>&lt;img src="http://kenji.blog/p/post-quantum-cryptography-algorithms-comparison/img/eyecatch.jpg" alt="Featured image of post 【Иллюстрированное руководство по PQC】Сравнение основных алгоритмов постквантовой криптографии" />&lt;h2 id="1-введение-криптографический-кризис-вызванный-квантовыми-компьютерами">1. Введение: «Криптографический кризис», вызванный квантовыми компьютерами
&lt;/h2>&lt;p>В современном интернет-обществе криптография с открытым ключом является незаменимой инфраструктурой для защиты конфиденциальности связи и целостности данных. Безопасность широко используемых сегодня алгоритмов RSA и криптографии на эллиптических кривых (ECC) опирается на математические барьеры — «сложность факторизации больших составных чисел» и «сложность задачи дискретного логарифмирования на эллиптических кривых» соответственно. Доказано, что классическим компьютерам (компьютерам, которые мы используем сейчас, включая суперкомпьютеры) для решения этих математических задач потребовалось бы время, превышающее возраст Вселенной, и это служило основой их безопасности.&lt;/p>
&lt;p>Однако эта надежная предпосылка может быть разрушена до основания из-за развития теории и практического применения &lt;strong>квантовых компьютеров&lt;/strong>. В 1994 году криптограф Питер Шор (Peter Shor) представил «&lt;strong>Алгоритм Шора&lt;/strong>», который теоретически доказал, что при запуске на отказоустойчивом универсальном квантовом компьютере (CRQC: Cryptographically Relevant Quantum Computer) достаточной мощности, задачи факторизации и дискретного логарифмирования могут быть решены за «полиномиальное время». Это означает, что все используемые в настоящее время алгоритмы криптографии с открытым ключом будут нейтрализованы.&lt;/p>
&lt;div class="mermaid">graph TD
A["Крупномасштабный квантовый компьютер (CRQC)"] -->|Запуск| B["Алгоритм Шора"]
B -->|Взлом за полиномиальное время| C["Задача факторизации (RSA)"]
B -->|Взлом за полиномиальное время| D["Задача дискретного логарифмирования (ECC / ECDSA)"]
C --> E["Прослушивание связи, подделка данных, спуфинг"]
D --> E
F["Store Now, Decrypt Later (SNDL)"] --> E&lt;/div>
&lt;p>Считать, что «полноценные квантовые компьютеры появятся еще через несколько десятилетий, поэтому проблем нет» — крайне опасно. Причина в том, что метод атаки, известный как &lt;strong>Store Now, Decrypt Later (SNDL: Сохрани сейчас, расшифруй потом)&lt;/strong>, уже стал реальной угрозой. Это атака, при которой враждебные государства или хакерские организации сохраняют огромные объемы текущих зашифрованных данных связи (например, TLS-трафик) в хранилища, чтобы расшифровать их все в тот момент, когда в будущем станет доступен мощный квантовый компьютер. Государственные секреты, инфраструктурная информация и медицинские данные, требующие долгосрочной защиты, уже подвержены этой угрозе.&lt;/p>
&lt;p>Кроме того, для симметричной криптографии (например, AES) и хеш-функций (например, SHA-256) существует &lt;strong>Алгоритм Гровера&lt;/strong>, открытый в 1996 году. Он сокращает вычислительную сложность атаки полным перебором (брутфорс) до квадратного корня. Другими словами, уровень безопасности AES-128 фактически снижается вдвое — до 2 в 64-й степени, поэтому в квантовую эру рекомендуется использовать более длинные ключи и хеши, такие как AES-256 и SHA-384.&lt;/p>
&lt;p>Для противодействия этому беспрецедентному криптографическому кризису была создана &lt;strong>постквантовая криптография (Post-Quantum Cryptography: PQC)&lt;/strong>, основанная на новых математических задачах, которые трудно решить даже с помощью квантовых компьютеров. В этой статье, основываясь на результатах процесса стандартизации PQC, проведенного Национальным институтом стандартов и технологий США (NIST), мы подробно рассмотрим основные алгоритмы PQC — от их математических основ до механизмов работы и сравнения архитектур.&lt;/p>
&lt;hr>
&lt;h2 id="2-обзор-и-история-проекта-по-стандартизации-pqc-от-nist">2. Обзор и история проекта по стандартизации PQC от NIST
&lt;/h2>&lt;p>Переход на новые криптографические технологии, включая перепроектирование протоколов, обновление систем и замену оборудования, занимает от нескольких лет до десятилетий. Поэтому криптографы во всем мире заранее начали исследования в области PQC. Центральную роль в этом сыграл NIST (Национальный институт стандартов и технологий) США. В 2016 году NIST объявил о начале процесса стандартизации PQC и начал принимать предложения совершенно новых криптографических алгоритмов от мирового криптографического сообщества.&lt;/p>
&lt;p>Объектами стандартизации стали две основные категории:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Криптография с открытым ключом / Механизм инкапсуляции ключа (KEM: Key Encapsulation Mechanism)&lt;/strong>: Механизм для безопасного обмена (доставки) симметричных ключей, используемых для шифрования каналов связи, например, при TLS-соединениях.&lt;/li>
&lt;li>&lt;strong>Цифровые подписи (Digital Signatures)&lt;/strong>: Механизм для проверки отсутствия фальсификации данных и предотвращения подмены отправителя (аутентичность) при обновлениях программного обеспечения или в цифровых сертификатах.&lt;/li>
&lt;/ol>
&lt;p>После почти шести лет интенсивной оценки, анализа и соревнований по взлому (раунды 1–3), а также дополнительной оценки некоторых алгоритмов в раунде 4, следующие алгоритмы были официально выпущены в качестве Федеральных стандартов обработки информации (FIPS) в 2024 году и утверждены как будущие мировые стандарты:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FIPS 203 (ML-KEM)&lt;/strong>: KEM на базе CRYSTALS-Kyber&lt;/li>
&lt;li>&lt;strong>FIPS 204 (ML-DSA)&lt;/strong>: Цифровая подпись на базе CRYSTALS-Dilithium&lt;/li>
&lt;li>&lt;strong>FIPS 205 (SLH-DSA)&lt;/strong>: Цифровая подпись на основе хешей без сохранения состояния на базе SPHINCS+&lt;/li>
&lt;li>&lt;strong>(Ожидается в будущем) FN-DSA&lt;/strong>: Цифровая подпись на базе FALCON&lt;/li>
&lt;/ul>
&lt;p>Выбранные алгоритмы опираются на различные математические «задачи о сложности», что обеспечивает разнообразие (Crypto Agility), чтобы в случае обнаружения критической уязвимости в одном алгоритме вся система не рухнула. В процессе стандартизации криптография на решетках (Lattice-based cryptography) стала главным героем с точки зрения производительности, но криптография на основе хешей и кодов была принята в качестве надежного резерва.&lt;/p>
&lt;hr>
&lt;h2 id="3-классификация-основных-математических-подходов-pqc">3. Классификация основных математических подходов PQC
&lt;/h2>&lt;p>Алгоритмы PQC в основном делятся на следующие пять категорий в зависимости от математических задач, служащих основой их безопасности. В этой статье мы подробно рассмотрим первые три.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Криптография на решетках (Lattice-based Cryptography)&lt;/strong>:
Основана на задаче нахождения кратчайшего вектора (SVP), задаче нахождения ближайшего вектора (CVP) в многомерном пространстве решеток, а также на производной от них задаче LWE. Это ядро стандартизации NIST, к которому относятся Kyber, Dilithium и FALCON. Они предлагают лучший баланс между скоростью обработки, размером открытого ключа и размером шифротекста, что делает их подходящими для общего использования.&lt;/li>
&lt;li>&lt;strong>Криптография на основе хешей (Hash-based Cryptography)&lt;/strong>:
Безопасность опирается исключительно на «устойчивость к коллизиям» и «односторонность» криптографических хеш-функций (таких как SHA-2 или SHAKE). Применима только для цифровых подписей (например, SPHINCS+), но характеризуется наиболее строгим доказательством безопасности и чрезвычайно высокой устойчивостью к неизвестным математическим атакам.&lt;/li>
&lt;li>&lt;strong>Криптография на основе кодов (Code-based Cryptography)&lt;/strong>:
Основана на теории кодов с исправлением ошибок и опирается на сложность задачи синдромного декодирования (Syndrome Decoding Problem). Типичным представителем является Classic McEliece, предложенный в 1970-х годах. Обладает долгой историей и доказанной безопасностью, но при этом имеет крайне большие размеры открытых ключей (в мегабайтах).&lt;/li>
&lt;li>&lt;strong>Многомерная полиномиальная криптография (Multivariate Polynomial Cryptography)&lt;/strong>:
Основана на сложности решения системы нелинейных уравнений со многими переменными над конечным полем (задача MQ). Предлагалась в основном для цифровых подписей (например, Rainbow), но во время финального раунда NIST был обнаружен мощный метод атаки, позволивший взломать ее на обычном ПК за несколько дней, из-за чего многие алгоритмы выбыли из процесса стандартизации.&lt;/li>
&lt;li>&lt;strong>Криптография на основе изогений (Isogeny-based Cryptography)&lt;/strong>:
Основана на задаче поиска пути в графах изогений эллиптических кривых. Имела очень маленький размер ключа и ожидалась как достойный преемник ECC, однако в 2022 году финалист «SIKE» был полностью взломан всего за несколько часов на обычном ПК с использованием классической математики (атака Castryck-Decru), что стало драматичным финалом, символизирующим сложность и опасность проектирования PQC.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-глубины-криптографии-на-решетках-задача-lwe-и-математические-основы-module-lwe">4. Глубины криптографии на решетках: задача LWE и математические основы Module-LWE
&lt;/h2>&lt;p>В настоящее время наиболее перспективной и ставшей ядром стандартизации является &lt;strong>криптография на решетках&lt;/strong>. В основе ее безопасности лежит &lt;strong>задача LWE (Learning with Errors: Обучение с ошибками)&lt;/strong>. Она была предложена Одедом Регевом (Oded Regev) в 2005 году, и за это прорывное достижение он был удостоен премии Гёделя. Невозможно говорить о современной PQC без понимания задачи LWE.&lt;/p>
&lt;h3 id="41-что-такое-задача-lwe-learning-with-errors">4.1. Что такое задача LWE (Learning with Errors)?
&lt;/h3>&lt;p>Сначала рассмотрим простую систему линейных уравнений. Предположим, по модулю $q$ даны известная случайная матрица $A$, неизвестный секретный вектор $\vec{s}$ и их произведение $\vec{b}$:&lt;/p>
$$ \vec{b} = A\vec{s} \pmod q $$
&lt;p>В этом случае, найти неизвестный $\vec{s}$ из открытой информации $A$ и $\vec{b}$ довольно просто. Используя классический алгоритм «метод Гаусса», можно легко вычислить $\vec{s}$ за полиномиальное время.&lt;/p>
&lt;p>Однако, если добавить к этому уравнению «небольшую преднамеренную ошибку (шум)», сложность задачи резко возрастает. Это и есть &lt;strong>задача LWE&lt;/strong>.&lt;/p>
&lt;p>Имеется неизвестный секретный вектор $\vec{s} \in \mathbb{Z}_q^n$ и случайно выбранная матрица $A \in \mathbb{Z}_q^{m \times n}$. Кроме того, подготавливается вектор ошибок $\vec{e} \in \mathbb{Z}_q^m$ с «достаточно малыми значениями элементов», выбранными в соответствии с нормальным или биномиальным распределением, и $\vec{b}$ вычисляется следующим образом:&lt;/p>
$$ \vec{b} = A\vec{s} + \vec{e} \pmod q $$
&lt;p>&lt;strong>Поисковая задача LWE (Search LWE)&lt;/strong> заключается в нахождении секретной информации $\vec{s}$ из открытых данных $(A, \vec{b})$. Из-за присутствия ошибки $\vec{e}$ попытки применить алгебраические методы, такие как метод Гаусса, терпят неудачу: в процессе сложения и вычитания уравнений ошибка $\vec{e}$ нарастает, как снежный ком, и в конечном итоге результат становится неотличимым от случайных значений.&lt;/p>
&lt;p>Величие задачи LWE заключается в наличии сильного теоретического доказательства (сведения), согласно которому, если не существует квантового алгоритма, способного решить задачи на решетках в худшем случае (Worst-case hardness), такие как GapSVP и SIVP, то задачу LWE невозможно решить в среднем случае (Average-case). Это означает, что даже случайно сгенерированные криптографические ключи обладают надежной безопасностью, подкрепленной теоретическими пределами.&lt;/p>
&lt;h3 id="42-радикальное-повышение-эффективности-с-помощью-ring-lwe-и-module-lwe">4.2. Радикальное повышение эффективности с помощью Ring-LWE и Module-LWE
&lt;/h3>&lt;p>Стандартная задача LWE (Standard LWE) имеет очень четкие основания для безопасности, но матрица $A$ становится слишком большой, а размер ключа достигает мегабайтов, что делает ее непрактичной. В связи с этим был предложен подход, использующий кольца многочленов (Polynomial Rings) для создания алгебраической структуры.&lt;/p>
&lt;p>В задаче &lt;strong>Ring-LWE&lt;/strong> вместо обычных векторов и матриц используются элементы (многочлены) некоего кольца многочленов $R_q$. В стандартах NIST обычно используется следующее круговое кольцо многочленов:&lt;/p>
$$ R_q = \mathbb{Z}_q[X]/(X^n + 1) $$
&lt;p>Здесь $n$ — степень двойки (например, 256), а $q$ — подходящее простое число. В этом кольце используются элементы $a, s, e \in R_q$ для вычисления $b = a \cdot s + e \pmod q$. Поскольку один многочлен $a$ имеет $n$ коэффициентов, данные можно значительно сжать. Кроме того, использование &lt;strong>NTT (Number Theoretic Transform: Теоретико-числовое преобразование)&lt;/strong>, версии быстрого преобразования Фурье (FFT) для конечных полей, позволяет сверхбыстро умножать многочлены с вычислительной сложностью $O(n \log n)$.&lt;/p>
&lt;p>Однако в случае с Ring-LWE существовали опасения, что «могут существовать неизвестные уязвимости из-за специфической алгебраической структуры кольца». Кроме того, при изменении уровня безопасности (эквивалентно AES-128, 192, 256 и т.д.) было необходимо изменять саму степень многочлена $n$, что влекло за собой инженерную проблему полного переписывания реализации, включая алгоритм NTT.&lt;/p>
&lt;p>В связи с этим алгоритмы, прошедшие стандартизацию (Kyber и Dilithium), приняли задачу &lt;strong>Module-LWE (M-LWE)&lt;/strong>. Module-LWE — это компромисс между бесструктурным Standard LWE и слишком структурированным Ring-LWE. Он использует матрицу (модуль) размера $k \times k$, элементами которой являются элементы кольца многочленов $R_q$:&lt;/p>
$$ \vec{b} = A\vec{s} + \vec{e} \pmod{R_q} \quad (A \in R_q^{k \times k}, \vec{s}, \vec{e} \in R_q^k) $$
&lt;p>Главное преимущество Module-LWE заключается в том, что при фиксированной степени многочлена $n$ (в стандартах NIST $n=256$) можно легко масштабировать уровень безопасности, просто изменяя размерность матрицы $k$.
Например, в Kyber размерность $k$ регулируется следующим образом:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Kyber512 (Level 1)&lt;/strong>: $k = 2$ (эквивалентно AES-128)&lt;/li>
&lt;li>&lt;strong>Kyber768 (Level 3)&lt;/strong>: $k = 3$ (эквивалентно AES-192)&lt;/li>
&lt;li>&lt;strong>Kyber1024 (Level 5)&lt;/strong>: $k = 4$ (эквивалентно AES-256)&lt;/li>
&lt;/ul>
&lt;p>Это позволило на 100% повторно использовать базовый код NTT и аппаратные схемы для полиномиальных вычислений на всех уровнях безопасности, что радикально повысило безопасность и эффективность реализации.&lt;/p>
&lt;hr>
&lt;h2 id="5-crystals-kyber-ml-kem-механизм-инкапсуляции-ключа-следующего-поколения">5. CRYSTALS-Kyber (ML-KEM): Механизм инкапсуляции ключа следующего поколения
&lt;/h2>&lt;p>Официально стандартизированный как &lt;strong>FIPS 203 (ML-KEM)&lt;/strong>, CRYSTALS-Kyber представляет собой механизм инкапсуляции ключа (KEM), основанный на задаче Module-LWE, описанной выше. В будущем он станет де-факто мировым стандартом для безопасного обмена сессионными ключами в TLS 1.3, SSH и других протоколах.&lt;/p>
&lt;h3 id="51-архитектура-kem-key-encapsulation-mechanism">5.1. Архитектура KEM (Key Encapsulation Mechanism)
&lt;/h3>&lt;p>В эру PQC стандартом станет не прямой подход, как в RSA, где «клиент создает симметричный ключ, шифрует его открытым ключом сервера и отправляет», а концепция инкапсуляции, называемая KEM.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Client as "Клиент (Alice)"
participant Server as "Сервер (Bob)"
Note over Client: "ML-KEM KeyGen()"
Client->>Client: "Генерирует секретный ключ (sk) и открытый ключ (pk)"
Client->>Server: "Отправляет открытый ключ (pk)"
Note over Server: "ML-KEM Encaps()"
Server->>Server: "Генерирует случайный симметричный ключ (K)"
Server->>Server: "Инкапсулирует K с помощью pk, создавая шифротекст (c)"
Server->>Client: "Отправляет шифротекст (c)"
Note over Client: "ML-KEM Decaps()"
Client->>Client: "Расшифровывает шифротекст (c) с помощью секретного ключа (sk)"
Client->>Client: "Снимает инкапсуляцию, извлекая симметричный ключ (K)"
Note over Client, Server: "Начинает защищенную связь (например, AES), используя общий симметричный ключ (K)"&lt;/div>
&lt;h3 id="52-внутренние-механизмы-алгоритма-kyber-и-преобразование-фудзисаки-окамото">5.2. Внутренние механизмы алгоритма Kyber и преобразование Фудзисаки-Окамото
&lt;/h3>&lt;p>Дизайн Kyber очень утонченный. Сначала создается схема криптографии с открытым ключом, устойчивая только к CPA (атака на основе выбранного открытого текста) — Kyber.CPAPKE. Затем к ней применяется мощный криптографический метод, известный как &lt;strong>преобразование Фудзисаки-Окамото (Fujisaki-Okamoto Transform)&lt;/strong>, благодаря которому схема превращается в полноценный KEM, устойчивый к CCA (адаптивная атака на основе выбранного шифротекста).&lt;/p>
&lt;p>Основной механизм шифрования и дешифрования CPAPKE выглядит следующим образом:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Генерация ключей (Key Generation)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Из случайного значения сида генерируется матрица $A \in R_q^{k \times k}$ в домене NTT. Используется модуль $q = 3329$.&lt;/li>
&lt;li>Из центрированного биномиального распределения (CBD) берутся секретный вектор $\vec{s}$ и вектор ошибок $\vec{e}$ с малыми коэффициентами.&lt;/li>
&lt;li>Вычисляется $\vec{t} = A\vec{s} + \vec{e}$. Открытый ключ — это $(A, \vec{t})$, секретный ключ — $\vec{s}$. (На практике $A$ раскрывается в виде сида для экономии пропускной способности).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Шифрование (Encryption)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>32-байтное сообщение $m$ (материал для симметричного ключа), которым необходимо обменяться, кодируется в многочлен.&lt;/li>
&lt;li>Генерируются новый случайный вектор $\vec{r}$ и малые ошибки $\vec{e_1}, e_2$.&lt;/li>
&lt;li>$\vec{u} = A^T\vec{r} + \vec{e_1}$&lt;/li>
&lt;li>$v = \vec{t}^T\vec{r} + e_2 + \lfloor q/2 \rceil \cdot m$&lt;/li>
&lt;li>Шифротекстом является пара $(\vec{u}, v)$.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Дешифрование (Decryption)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Получатель вычисляет $v - \vec{s}^T\vec{u}$.&lt;/li>
&lt;li>При разложении формулы получаем:
$v - \vec{s}^T\vec{u} = (\vec{t}^T\vec{r} + e_2 + \lfloor q/2 \rceil \cdot m) - \vec{s}^T(A^T\vec{r} + \vec{e_1})$&lt;/li>
&lt;li>Подставив $\vec{t} = A\vec{s} + \vec{e}$, главный член $\vec{s}^TA^T\vec{r}$ сокращается.&lt;/li>
&lt;li>Остается: $\lfloor q/2 \rceil \cdot m + (\vec{e}^T\vec{r} + e_2 - \vec{s}^T\vec{e_1})$.&lt;/li>
&lt;li>Члены в скобках — это «произведения и суммы малых ошибок», поэтому в целом они остаются достаточно малыми (шумом). Следовательно, путем проверки того, близок ли каждый коэффициент к $0$ или к $q/2$, можно полностью и безошибочно восстановить биты (0 или 1) исходного сообщения $m$.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Главными преимуществами Kyber являются его потрясающая &lt;strong>скорость обработки&lt;/strong> и &lt;strong>умеренный размер ключа&lt;/strong>. Для Kyber768 размер открытого ключа составляет 1184 байта, а размер шифротекста — 1088 байт. И хотя они больше по сравнению с RSA-3072 (размер ключа около 384 байт), они помещаются в MTU (Maximum Transmission Unit) современных интернет-сетей без фрагментации пакетов и практически не влияют на сетевые задержки.&lt;/p>
&lt;hr>
&lt;h2 id="6-crystals-dilithium-ml-dsa-универсальная-цифровая-подпись-на-основе-решеток">6. CRYSTALS-Dilithium (ML-DSA): Универсальная цифровая подпись на основе решеток
&lt;/h2>&lt;p>В процессе стандартизации цифровых подписей соревновались алгоритмы с различными концептуальными дизайнами в рамках подхода криптографии на решетках. Среди них в качестве универсальной цифровой подписи &lt;strong>FIPS 204 (ML-DSA)&lt;/strong> был выбран &lt;strong>CRYSTALS-Dilithium&lt;/strong>.&lt;/p>
&lt;h3 id="61-парадигма-fiat-shamir-with-aborts">6.1. Парадигма Fiat-Shamir with Aborts
&lt;/h3>&lt;p>Dilithium, как и Kyber, является схемой цифровой подписи на основе Module-LWE (а также задачи Module-SIS). В основе его дизайна лежит критически важная парадигма «&lt;strong>Fiat-Shamir with Aborts (Преобразование Фиата-Шамира с прерываниями)&lt;/strong>».&lt;/p>
&lt;p>Само по себе преобразование Фиата-Шамира является стандартным методом для преобразования интерактивного протокола доказательства с нулевым разглашением в неинтерактивную цифровую подпись. Доказывающий (подписант) генерирует обязательство $y$, вычисляет $w = Ay$ и пропускает через хеш-функцию, чтобы получить случайный запрос $c$, и вычисляет ответ $z = y + cs$.&lt;/p>
&lt;p>Однако при простом применении этого метода в криптографии на решетках распределение ответа $z$ искажается в зависимости от значения секретного ключа $s$, что приводит к критической проблеме (математическая утечка типа побочного канала), когда атакующий, анализируя множество подписей, постепенно получает информацию о секретном ключе $s$.&lt;/p>
&lt;p>Команда разработчиков Dilithium (Любашевский и др.) внедрила метод «&lt;strong>выборки с отклонением (Rejection Sampling)&lt;/strong>», при котором, если коэффициенты вычисленного результата подписи $z$ не попадают в заранее заданный безопасный пороговый диапазон, весь процесс подписания прерывается (Abort), и вычисления начинаются заново с новым случайным числом $y$.&lt;/p>
&lt;p>В результате окончательно выдаваемая подпись $z$ имеет абсолютно равномерное распределение, не зависящее от секретного ключа, что математически полностью предотвращает утечку информации.&lt;/p>
&lt;h3 id="62-преимущества-dilithium-и-простота-реализации">6.2. Преимущества Dilithium и простота реализации
&lt;/h3>&lt;p>Огромное преимущество дизайна Dilithium заключается в том, что в процессе создания подписи &lt;strong>вообще не используется&lt;/strong> сложная «выборка из гауссовского распределения» или «вычисления с плавающей запятой». Поскольку алгоритм может быть реализован с использованием только выборки из равномерного распределения, простых модульных целочисленных вычислений, NTT и хеш-функций (SHAKE), его легко безопасно реализовать за константное время (Constant-time) в самых разных средах — от встроенных микроконтроллеров до облачных серверов. Это также обеспечивает надежную защиту от физических атак по сторонним каналам, таких как атаки по времени.&lt;/p>
&lt;hr>
&lt;h2 id="7-falcon-fn-dsa-ультракомпактная-решеточная-подпись">7. FALCON (FN-DSA): Ультракомпактная решеточная подпись
&lt;/h2>&lt;p>NIST выбрал &lt;strong>FALCON (Fast-Fourier Lattice-based Compact Signatures over NTRU)&lt;/strong> как еще одного кандидата на стандартизацию подписи на решетках, обладающего характеристиками, отличными от Dilithium (в настоящее время разрабатывается черновик стандарта FN-DSA).&lt;/p>
&lt;h3 id="71-решетки-ntru-и-гауссовская-выборка">7.1. Решетки NTRU и гауссовская выборка
&lt;/h3>&lt;p>Главной особенностью FALCON является использование исторически более старых (существующих с 1996 года) &lt;strong>решеток NTRU (N-th degree Truncated polynomial Ring Units)&lt;/strong> вместо задачи LWE. Кроме того, в нем используется парадигма «&lt;strong>Hash-and-Sign (Хешируй и подписывай)&lt;/strong>», основанная на фреймворке GPV (Gentry-Peikert-Vaikuntanathan).&lt;/p>
&lt;p>В Hash-and-Sign хеш сообщения служит целевой точкой в пространстве, а подписью является нахождение ближайшей к ней точки решетки (приближенное решение задачи нахождения ближайшего вектора). Для этого требуется выборка точек в соответствии с дискретным гауссовским распределением, используя секретный ключ — «качественный короткий базис».&lt;/p>
&lt;p>FALCON радикально ускорил эти тяжелые вычисления с помощью метода, известного как «&lt;strong>Быстрая ортогонализация Фурье (Fast Fourier Orthogonalization: FFO)&lt;/strong>».&lt;/p>
&lt;h3 id="72-преимущества-и-недостатки-falcon">7.2. Преимущества и недостатки FALCON
&lt;/h3>&lt;p>Огромным преимуществом FALCON является его &lt;strong>чрезвычайно маленький размер подписи и открытого ключа (компактность)&lt;/strong>. В то время как размер подписи Dilithium3 составляет около 3309 байт, размер подписи FALCON-512 — всего около 666 байт. Открытый ключ также очень мал — 897 байт. Это делает его настоящим спасением в условиях с жесткими ограничениями пропускной способности, для IoT-устройств и специфических сетевых протоколов.&lt;/p>
&lt;p>Однако существует и серьезный недостаток. Поскольку при генерации подписи требуется дискретная гауссовская выборка, включающая сложные &lt;strong>вычисления с плавающей запятой (64-bit IEEE 754)&lt;/strong>, реализовать ее за константное время (Constant-time implementation) для предотвращения утечек по времени крайне сложно, и размер кода становится огромным. По этой причине FALCON позиционируется как мощный специализированный алгоритм для конкретных применений, в отличие от универсального Dilithium.&lt;/p>
&lt;div class="mermaid">graph LR
A["Требования к цифровой подписи"] --> B{"Каковы главные ограничения?"}
B -->|"Простота реализации, универсальность, простота реализации константного времени"| C["Dilithium (ML-DSA)"]
B -->|"Минимизация полосы пропускания связи, компактность размера данных"| D["FALCON (FN-DSA)"]
C --> E["Универсальные сертификаты TLS, электронная подпись программного обеспечения"]
D --> F["Протоколы со строгими ограничениями на размер пакета, специальные среды"]&lt;/div>
&lt;hr>
&lt;h2 id="8-sphincs-slh-dsa-подпись-на-основе-хешей-с-высочайшим-уровнем-безопасности">8. SPHINCS+ (SLH-DSA): Подпись на основе хешей с высочайшим уровнем безопасности
&lt;/h2>&lt;p>На случай худшего сценария, при котором безопасность решеточной криптографии будет нарушена в будущем из-за прорыва гениальных математиков, NIST разработал стандарт &lt;strong>FIPS 205 (SLH-DSA)&lt;/strong>, или &lt;strong>SPHINCS+&lt;/strong>, с совершенно иным подходом.&lt;/p>
&lt;p>SPHINCS+ относится к категории &lt;strong>подписей на основе хешей&lt;/strong>. Его безопасность опирается исключительно на один факт: «используемая криптографическая хеш-функция (например, SHA-2 или SHAKE256) обладает устойчивостью к коллизиям и односторонностью». Поскольку он не зависит от математических задач с определенной алгебраической структурой, таких как LWE или факторизация, какие бы мощные квантовые алгоритмы ни появились в будущем, им можно будет противостоять простым увеличением длины выхода хеш-функции. Это обеспечивает чрезвычайно надежную безопасность (наиболее консервативную защиту).&lt;/p>
&lt;h3 id="81-архитектура-без-сохранения-состояния-stateless-с-использованием-wots-и-fors">8.1. Архитектура без сохранения состояния (stateless) с использованием WOTS+ и FORS
&lt;/h3>&lt;p>История подписей на основе хешей давняя и восходит к подписи Лэмпорта и одноразовой подписи Винтерница (WOTS) в 1970-х годах. Это были одноразовые ключи, позволяющие «безопасно подписать только один раз». Чтобы сделать возможным их многократное использование, были разработаны алгоритмы XMSS (eXtended Merkle Signature Scheme) и LMS, которые с помощью деревьев Меркла (Merkle Tree) управляли бесчисленным количеством одноразовых ключей под одним корневым хешем.&lt;/p>
&lt;p>Однако у XMSS и LMS был серьезный недостаток — они были «&lt;strong>stateful (с сохранением состояния)&lt;/strong>». При каждом создании подписи необходимо было строго фиксировать в энергонезависимой памяти индекс («какой по счету одноразовый ключ был использован»). Если в результате восстановления снимка виртуальной машины состояние откатывалось назад, и один и тот же ключ использовался дважды, секретный ключ мгновенно утекал, и система рушилась.&lt;/p>
&lt;p>SPHINCS+ решает эту проблему управления состоянием и является &lt;strong>stateless (не требующей сохранения состояния)&lt;/strong> подписью на основе хешей.
В основе его технологии лежит следующая комбинация:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>WOTS+ (Winternitz One-Time Signature Plus)&lt;/strong>: Базовая одноразовая подпись.&lt;/li>
&lt;li>&lt;strong>FORS (Forest of Random Subsets)&lt;/strong>: Технология подписи с ограниченным числом использований (Few-Time Signature). Сохраняет безопасность, даже если один и тот же ключ используется несколько раз.&lt;/li>
&lt;li>&lt;strong>Hyper-Tree (Гипердерево)&lt;/strong>: Гигантская структура, состоящая из многоуровнево вложенных деревьев Меркла.&lt;/li>
&lt;/ol>
&lt;p>При создании подписи в SPHINCS+ вместо управления состоянием используется псевдослучайное число для случайного выбора одного из огромного числа ключей FORS в основании Hyper-Tree. Поскольку количество листьев дерева астрономически велико, вероятность случайного выбора одного и того же ключа дважды (коллизия) ничтожно мала, что в итоге позволяет реализовать подход без сохранения состояния.&lt;/p>
&lt;p>Единственным и самым большим недостатком SPHINCS+ является &lt;strong>очень большой размер подписи&lt;/strong>. В зависимости от параметров он может достигать от 17 КБ до 49 КБ, а скорость генерации подписи значительно ниже по сравнению с решеточной криптографией. Поэтому ожидается, что алгоритм будет применяться не в повседневном веб-браузинге, а в сценариях, требующих долгосрочной абсолютной безопасности и не требующих частого подписания, например, в подписях обновлений ПО или сертификатах корневых удостоверяющих центров (CA).&lt;/p>
&lt;hr>
&lt;h2 id="9-криптография-на-основе-кодов-старый-добрый-гигант-classic-mceliece">9. Криптография на основе кодов: старый добрый гигант Classic McEliece
&lt;/h2>&lt;p>В процессе стандартизации NIST важным подходом, который все еще оценивается в качестве финалиста Раунда 4, является алгоритм &lt;strong>Classic McEliece&lt;/strong>, относящийся к &lt;strong>криптографии на основе кодов&lt;/strong>.&lt;/p>
&lt;p>Предложенный Робертом МакЭлисом (Robert McEliece) в 1978 году, этот алгоритм является одним из старейших в истории криптографии с открытым ключом, наряду с RSA. Он использует алгебро-геометрические коды, называемые «кодами Гоппы», и основан на «&lt;strong>задаче синдромного декодирования (Syndrome Decoding Problem)&lt;/strong>»: к сообщению преднамеренно добавляется ошибка (вектор шума) для его шифрования, а восстановить исходное сообщение, удалив ошибку с помощью мощных возможностей исправления, может только тот, у кого в качестве секретного ключа есть проверочная матрица кода Гоппы.&lt;/p>
$$ \vec{c} = \vec{m} G + \vec{e} $$
&lt;p>
(где $G$ — скремблированная порождающая матрица, являющаяся открытым ключом, а $\vec{e}$ — вектор ошибки веса $t$)&lt;/p>
&lt;p>Удивительно в Classic McEliece то, что &lt;strong>прошло более 40 лет с момента его создания, он подвергался интенсивным исследованиям по взлому криптографами со всего мира, но при этом ни разу не была обнаружена его фундаментальная уязвимость&lt;/strong> — это колоссальный послужной список. Из всех алгоритмов PQC он обладает самой «проверенной временем надежной безопасностью».&lt;/p>
&lt;p>Кроме того, есть преимущество: размер шифротекста очень мал (всего около 100–200 байт). Однако у него есть критический недостаток — &lt;strong>размер открытого ключа измеряется мегабайтами (МБ)&lt;/strong>. Даже на самом низком уровне безопасности (эквивалентно AES-128) открытый ключ составляет около 250 КБ, а на высоких уровнях превышает 1 МБ.&lt;/p>
&lt;p>По этой причине он совершенно не подходит для таких сценариев, как рукопожатие TLS, где открытый ключ передается по сети при каждом соединении. Однако для обмена предварительно общими ключами VPN, жесткого кодирования открытых ключей в прошивку или в спутниковой связи — особых случаях, где открытые ключи могут быть предварительно размещены в системе, он продолжает рассматриваться как крайне перспективный вариант благодаря своей высокой безопасности.&lt;/p>
&lt;hr>
&lt;h2 id="10-сравнение-производительности-и-компромиссы-алгоритмов-pqc">10. Сравнение производительности и компромиссы алгоритмов PQC
&lt;/h2>&lt;p>В таблице ниже приведены характеристики производительности основных алгоритмов, обсуждавшихся до сих пор, на общих уровнях безопасности (эквивалентно NIST Level 2–3, уровень AES-128–192).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Алгоритм (Стандартное имя)&lt;/th>
&lt;th style="text-align:left">Категория&lt;/th>
&lt;th style="text-align:left">Математическая основа&lt;/th>
&lt;th style="text-align:left">Размер открытого ключа&lt;/th>
&lt;th style="text-align:left">Размер секретного ключа&lt;/th>
&lt;th style="text-align:left">Размер шифротекста/подписи&lt;/th>
&lt;th style="text-align:left">Тенденция скорости обработки&lt;/th>
&lt;th style="text-align:left">Основные характеристики и применения&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Kyber768&lt;/strong>&lt;br>(ML-KEM)&lt;/td>
&lt;td style="text-align:left">KEM&lt;/td>
&lt;td style="text-align:left">Module-LWE&lt;/td>
&lt;td style="text-align:left">1,184 Bytes&lt;/td>
&lt;td style="text-align:left">2,400 Bytes&lt;/td>
&lt;td style="text-align:left">1,088 Bytes&lt;/td>
&lt;td style="text-align:left">Очень высокая&lt;/td>
&lt;td style="text-align:left">Лучший баланс размера ключа и скорости. Универсальный стандарт KEM для TLS 1.3 и др.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Dilithium3&lt;/strong>&lt;br>(ML-DSA)&lt;/td>
&lt;td style="text-align:left">Подпись&lt;/td>
&lt;td style="text-align:left">Module-LWE&lt;/td>
&lt;td style="text-align:left">1,952 Bytes&lt;/td>
&lt;td style="text-align:left">4,032 Bytes&lt;/td>
&lt;td style="text-align:left">3,309 Bytes&lt;/td>
&lt;td style="text-align:left">Генерация и верификация быстрые&lt;/td>
&lt;td style="text-align:left">Простая реализация. Универсальный стандарт цифровой подписи.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>FALCON-512&lt;/strong>&lt;br>(FN-DSA)&lt;/td>
&lt;td style="text-align:left">Подпись&lt;/td>
&lt;td style="text-align:left">Решетка NTRU&lt;/td>
&lt;td style="text-align:left">897 Bytes&lt;/td>
&lt;td style="text-align:left">1,281 Bytes&lt;/td>
&lt;td style="text-align:left">666 Bytes&lt;/td>
&lt;td style="text-align:left">Медленная генерация подписи, сверхбыстрая верификация&lt;/td>
&lt;td style="text-align:left">Минимальный размер подписи. Требует вычислений с плавающей запятой. Для встраиваемых систем и IoT.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>SPHINCS+&lt;/strong>&lt;br>(SLH-DSA)&lt;/td>
&lt;td style="text-align:left">Подпись&lt;/td>
&lt;td style="text-align:left">Хеш-функция&lt;/td>
&lt;td style="text-align:left">32 Bytes&lt;/td>
&lt;td style="text-align:left">64 Bytes&lt;/td>
&lt;td style="text-align:left">Ок. 17,000 Bytes&lt;/td>
&lt;td style="text-align:left">Очень медленная генерация&lt;/td>
&lt;td style="text-align:left">Риск математического взлома близок к нулю. Для высокосекретных применений (корневые сертификаты).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Classic McEliece&lt;/strong>&lt;/td>
&lt;td style="text-align:left">KEM&lt;/td>
&lt;td style="text-align:left">Код Гоппы&lt;/td>
&lt;td style="text-align:left">&lt;strong>Ок. 1.04 MB&lt;/strong>&lt;/td>
&lt;td style="text-align:left">13,568 Bytes&lt;/td>
&lt;td style="text-align:left">&lt;strong>188 Bytes&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Быстрая инкапсуляция&lt;/td>
&lt;td style="text-align:left">40 лет доказанной безопасности. Огромный открытый ключ. Для сред с возможностью хардкодинга.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="понимание-компромиссов">Понимание компромиссов
&lt;/h3>&lt;p>В мире PQC не существует единого волшебного алгоритма, который был бы «маленьким, быстрым и имел безупречные математические гарантии».&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Стандарт интернета (Kyber / Dilithium)&lt;/strong>: Предлагает наилучший баланс производительности и лучше всего подходит для прозрачной замены (drop-in replacement) текущих RSA/ECC.&lt;/li>
&lt;li>&lt;strong>Предельный консерватизм (SPHINCS+)&lt;/strong>: Выбирается в тех случаях, когда требуется абсолютная страховка от будущих математических прорывов, даже ценой увеличения размера данных и снижения скорости.&lt;/li>
&lt;li>&lt;strong>Для особых сред (FALCON / Classic McEliece)&lt;/strong>: Специализированное оружие, выбираемое в зависимости от ограничений среды — например, при крайне узкой полосе пропускания связи или при возможности предварительного распространения ключей.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-проблемы-практического-применения-и-реалистичное-решение-гибридная-криптография">11. Проблемы практического применения и реалистичное решение: «Гибридная криптография»
&lt;/h2>&lt;p>С завершением стандартизации NIST и официальным выпуском стандартов FIPS, в IT-инфраструктуре по всему миру стартовал полномасштабный переход на PQC (&lt;strong>PQC-миграция&lt;/strong>). Браузер Google Chrome, сервис Apple iMessage (протокол PQ3) и сетевые провайдеры вроде Cloudflare уже внедрили поддержку PQC в свои протоколы и начали их практическое использование.&lt;/p>
&lt;p>Однако полный и резкий переход на новые криптографические алгоритмы сопряжен с очень высокими рисками. Если предположить, что через несколько лет какой-нибудь гениальный математик обнаружит критический метод атаки на решеточные алгоритмы, такие как Kyber (математический дефект, позволяющий взломать его даже на классическом компьютере), то вся система, зависящая от него, мгновенно окажется уязвимой.&lt;/p>
&lt;p>Реалистичным и рекомендуемым подходом для смягчения риска этой неопределенности является «&lt;strong>Гибридная криптография (Hybrid Cryptography)&lt;/strong>».&lt;/p>
&lt;p>В гибридной криптографии для обмена ключами одновременно используются как проверенные годами классические алгоритмы (например, эллиптическая кривая X25519), так и новые PQC-алгоритмы (например, Kyber768). Каждый алгоритм генерирует компоненты симметричного ключа независимо друг от друга, а затем эти два компонента смешиваются с помощью безопасной функции формирования ключа (KDF) для создания окончательного мастер-ключа.&lt;/p>
&lt;div class="mermaid">graph TD
A["Клиент"] -->|① Отправляет открытый ключ X25519 + открытый ключ Kyber| B["Сервер"]
B -->|② Отправляет общий ключ X25519 + инкапсулированный шифротекст Kyber| A
A --> C{"Вывод мастер-ключа (KDF)"}
B --> C
C -->|Вход: (симметричный ключ X25519) || (симметричный ключ Kyber)| D["Защищенный ключ связи (AES-256 / ChaCha20)"]
D -->|"Устойчивость к квантовым угрозам И классическим уязвимостям"| E["Защищенная гибридная зашифрованная связь (TLS 1.3)"]&lt;/div>
&lt;p>Это обеспечивает надежную двухуровневую безопасность: «в случае появления квантового компьютера и взлома ECC, Kyber защитит связь», и наоборот: «в случае обнаружения неизвестного математического дефекта в Kyber, ECC защитит связь». Ярким примером является проект стандарта &lt;strong>X25519MLKEM768 (ранее X25519Kyber768)&lt;/strong>, разрабатываемый в IETF; в настоящее время обмен данными между современными веб-браузерами и серверами осуществляется именно по этому гибридному методу.&lt;/p>
&lt;p>Кроме того, в системном проектировании концепция &lt;strong>Crypto Agility (криптографической гибкости)&lt;/strong> — архитектура, которая «не слишком зависит от конкретного криптографического алгоритма и может быстро переключиться на другой алгоритм в случае его компрометации (например, от Kyber к McEliece или от Dilithium к SPHINCS+)» — станет обязательным требованием при разработке будущих систем.&lt;/p>
&lt;hr>
&lt;h2 id="12-заключение-новые-горизонты-криптографии">12. Заключение: Новые горизонты криптографии
&lt;/h2>&lt;p>Квантовый компьютер, технология-мечта человечества, по иронии судьбы стал величайшей угрозой, способной разрушить математические барьеры «факторизации» и «дискретного логарифмирования», на которые мы полагались долгие годы. Однако криптографы со всего мира не сдались и освоили более сложные и глубокие многомерные математические области, такие как теория решеток, деревья хеш-функций и коды, исправляющие ошибки, построив новый барьер в виде постквантовой криптографии (PQC).&lt;/p>
&lt;p>Завершение стандартизации алгоритмов FIPS 203 (ML-KEM), FIPS 204 (ML-DSA) и FIPS 205 (SLH-DSA) институтом NIST — это не финиш. Это лишь первый шаг в грандиозном путешествии PQC-миграции, которое продлится десятилетия. Для инженеров-программистов и системных архитекторов главной технической задачей в будущем станет то, как оптимально адаптировать сетевые протоколы и системы к «увеличению размера ключей» и «изменениям вычислительных затрат», вызванных этими новыми алгоритмами.&lt;/p>
&lt;p>Битва между квантовыми компьютерами и криптографией — это захватывающая область, где наиболее интенсивно пересекаются математические поиски человечества и технологическая эволюция. Я надеюсь, что благодаря этой статье вы смогли глубоко понять красивую математическую теорию, стоящую за PQC, и удивительные механизмы каждого из алгоритмов, формирующих будущее кибербезопасности.&lt;/p>
&lt;hr>
&lt;p>&lt;em>References:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>NIST Post-Quantum Cryptography Standardization Program&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 204: Module-Lattice-Based Digital Signature Standard&lt;/em>&lt;/li>
&lt;li>&lt;em>FIPS 205: Stateless Hash-Based Digital Signature Standard&lt;/em>&lt;/li>
&lt;/ul></description></item></channel></rss>