<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Mathematics on kenji.blog</title><link>http://kenji.blog/ru/categories/mathematics/</link><description>Recent content in Mathematics 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/mathematics/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>Гипотеза Римана и распределение простых чисел: глубокая связь с современной криптографией</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>Быстрый алгоритм проверки на простоту, реализованный на C++ (метод Миллера-Рабина и др.)</title><link>http://kenji.blog/ru/p/cpp-fast-prime-testing-miller-rabin/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/cpp-fast-prime-testing-miller-rabin/</guid><description>&lt;img src="http://kenji.blog/p/cpp-fast-prime-testing-miller-rabin/img/eyecatch.jpg" alt="Featured image of post Быстрый алгоритм проверки на простоту, реализованный на C++ (метод Миллера-Рабина и др.)" />&lt;h1 id="введение-почему-нужна-быстрая-проверка-на-простоту">Введение: Почему нужна быстрая проверка на простоту
&lt;/h1>&lt;p>В мире информатики, теории криптографии или спортивного программирования, определение того, «является ли число простым», — это очень важная и фундаментальная задача. Например, криптография с открытым ключом, такая как алгоритм RSA, обеспечивающая безопасность современного интернета, основана на генерации огромных простых чисел и сложности их умножения (факторизации). Поэтому способность мгновенно определять, является ли огромное число простым, можно смело назвать технологией, лежащей в основе цифрового общества.&lt;/p>
&lt;p>Кроме того, в спортивном программировании (AtCoder, Codeforces и др.) проверка на простоту — часто встречающаяся тема. Для огромных входных данных с ограничениями вида $N \le 10^{18}$, когда требуется десятки тысяч раз проверить числа на простоту за 1 секунду, традиционные наивные алгоритмы не уложатся в лимит времени (Time Limit Exceeded: TLE).&lt;/p>
&lt;p>В этой статье мы подробно разберем всё от математических основ до высокооптимизированной реализации на C++: начиная с наивного алгоритма, вероятностного метода проверки простоты «тест Ферма» и заканчивая практичным сверхбыстрым алгоритмом «Тест Миллера-Рабина (Miller-Rabin)», который преодолевает его слабые стороны. В частности, для 64-битных целых чисел ($N &lt; 2^{64}$) мы подробно объясним метод «100% достоверной проверки (детерминированный алгоритм)», выходящий за рамки вероятностной оценки, и предоставим готовый к использованию исходный код на C++.&lt;/p>
&lt;hr>
&lt;h1 id="1-основы-проверки-на-простоту-и-метод-пробных-делений-trial-division">1. Основы проверки на простоту и метод пробных делений (Trial Division)
&lt;/h1>&lt;p>Простое число (Prime number) — это натуральное число больше 1, которое не имеет положительных делителей, кроме 1 и самого себя. Строго следуя определению, чтобы проверить, является ли целое число $N$ простым, можно попробовать разделить $N$ на все целые числа от $2$ до $N-1$. Если оно ни разу не разделится без остатка, то это простое число, а если разделится хотя бы раз — составное число (не простое).&lt;/p>
&lt;p>Однако вычислительная сложность этого метода составляет $O(N)$, и если $N$ — огромное число, например $10^{18}$, то даже на современном компьютере вычисления займут колоссальное количество времени.&lt;/p>
&lt;h2 id="оптимизация-метода-пробных-делений-поиск-до-sqrtn">Оптимизация метода пробных делений: поиск до $\sqrt{N}$
&lt;/h2>&lt;p>Если составное число $N$ можно выразить как $a \times b = N$ (где $a \le b$), то обязательно $a \le \sqrt{N}$. Следовательно, цикл проверки не нужно выполнять до $N-1$, достаточно проверить до $\sqrt{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;/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">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Проверка на простоту методом пробных делений (O(sqrt(N)))
&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">is_prime_trial_division&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">n&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">n&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">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&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">n&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="c1">// Проверяем только нечетные числа, начиная с 3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">2&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">n&lt;/span> &lt;span class="o">%&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="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="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="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Вычислительная сложность этого алгоритма — $O(\sqrt{N})$. Для $N \le 10^{12}$ вычисления выполняются мгновенно, но при $N \approx 10^{18}$ количество итераций цикла составит около $10^9$, что даже в C++ потребует от сотен миллисекунд до нескольких секунд. Поэтому этот метод не подходит для многократных проверок.&lt;/p>
&lt;hr>
&lt;h1 id="2-тест-ферма-начало-вероятностной-проверки-на-простоту">2. Тест Ферма: Начало вероятностной проверки на простоту
&lt;/h1>&lt;p>Чтобы преодолеть ограничения метода пробных делений, были придуманы «вероятностные алгоритмы» (Probabilistic Algorithm), использующие теоремы теории чисел. Ярким примером является «Тест Ферма на простоту» (Fermat Primality Test), основанный на малой теореме Ферма.&lt;/p>
&lt;h2 id="малая-теорема-ферма-fermats-little-theorem">Малая теорема Ферма (Fermat&amp;rsquo;s Little Theorem)
&lt;/h2>&lt;p>Эта теорема, открытая Пьером де Ферма (Pierre de Fermat), утверждает следующее:&lt;/p>
&lt;blockquote>
&lt;p>Для любого простого числа $p$ и любого целого числа $a$, взаимно простого с $p$ (не кратного $p$), выполняется следующее сравнение:
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>Взяв контрапозицию этой теоремы, можно сказать: «Если для некоторого целого числа $N$ и взаимно простого с ним целого числа $a$ выполняется $a^{N-1} \not\equiv 1 \pmod N$, то $N$ гарантированно является составным». Тест Ферма использует это свойство: для проверяемого числа $N$ выбирается случайное основание (base) $a$, вычисляется $a^{N-1} \pmod N$ и проверяется, равно ли оно $1$.&lt;/p>
&lt;h2 id="быстрое-возведение-в-степень-по-модулю-метод-бинарного-возведения-в-степень">Быстрое возведение в степень по модулю (Метод бинарного возведения в степень)
&lt;/h2>&lt;p>Для выполнения теста Ферма необходимо быстро вычислить огромную степень $a^{N-1} \pmod N$. Для этого используется «метод бинарного возведения в степень» (Modular Exponentiation / Binary Exponentiation). Его вычислительная сложность составляет $O(\log 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;/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">// Вычисление 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">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&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">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">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">long&lt;/span> &lt;span class="kt">long&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">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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;amp;&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="p">(&lt;/span>&lt;span class="n">__int128_t&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">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="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&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="n">b&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&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="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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>Здесь для предотвращения переполнения используется расширение GCC/Clang &lt;code>__int128_t&lt;/code> (128-битное целое число) для хранения промежуточных произведений.&lt;/li>
&lt;/ul>
&lt;h2 id="псевдопростые-числа-и-числа-кармайкла-carmichael-numbers">Псевдопростые числа и числа Кармайкла (Carmichael Numbers)
&lt;/h2>&lt;p>Тест Ферма очень мощный, но у него есть фатальный недостаток. Существуют такие составные числа $N$, для которых выполняется $a^{N-1} \equiv 1 \pmod N$ при всех $a$, взаимно простых с $N$.&lt;/p>
&lt;p>Такие числа называются «абсолютно псевдопростыми» или «числами Кармайкла». Наименьшее число Кармайкла — $561 = 3 \times 11 \times 17$.
Из-за существования чисел Кармайкла с помощью только теста Ферма невозможно провести детерминированную проверку со «100% вероятностью». Сколько бы различных $a$ мы ни пробовали, такие числа, как $561$, всегда будут притворяться (обманывать) простыми.&lt;/p>
&lt;hr>
&lt;h1 id="3-тест-простоты-миллера-рабина-miller-rabin-primality-test">3. Тест простоты Миллера-Рабина (Miller-Rabin Primality Test)
&lt;/h1>&lt;p>Алгоритм, блестяще преодолевший недостаток теста Ферма (существование чисел Кармайкла), — это «тест простоты Миллера-Рабина», предложенный Миллером (Gary L. Miller) и Рабином (Michael O. Rabin).
В настоящее время он наиболее широко используется как практичный и быстрый алгоритм проверки на простоту во встроенных библиотеках различных языков программирования и при генерации ключей криптографических систем.&lt;/p>
&lt;h2 id="математический-принцип">Математический принцип
&lt;/h2>&lt;p>Алгоритм Миллера-Рабина, помимо малой теоремы Ферма, использует следующее свойство: «В поле вычетов по простому модулю ($\mathbb{Z}/p\mathbb{Z}$) решениями уравнения $x^2 \equiv 1 \pmod p$ могут быть только $x \equiv 1$ или $x \equiv -1$» (в случае составного модуля могут существовать и другие, нетривиальные квадратные корни).&lt;/p>
&lt;p>Если из проверяемого нечетного числа $N$ вычесть $1$, то $N-1$ обязательно станет четным. Поэтому мы делим $N-1$ на $2$ столько раз, сколько возможно, и представляем в следующем виде:
&lt;/p>
$$ N-1 = d \cdot 2^s $$
&lt;p>
(где $d$ — нечетное, $s \ge 1$)&lt;/p>
&lt;p>Для любого основания $a$ ($1 &lt; a &lt; N-1$) мы проверяем, выполняется ли $a^{N-1} \equiv 1 \pmod N$ согласно малой теореме Ферма, но вычисляем это поэтапно.
Конкретно, мы последовательно возводим в квадрат: $a^d, a^{d \cdot 2}, a^{d \cdot 4}, \ldots, a^{d \cdot 2^s}$.&lt;/p>
&lt;p>Условием для того, чтобы тест Миллера-Рабина определил $N$ как «простое (или с высокой вероятностью простое)», является выполнение &lt;strong>любого&lt;/strong> из следующих утверждений:&lt;/p>
&lt;ol>
&lt;li>$a^d \equiv 1 \pmod N$&lt;/li>
&lt;li>Существует некоторое $r$ ($0 \le r &lt; s$), такое что $a^{d \cdot 2^r} \equiv -1 \pmod N$.
&lt;ul>
&lt;li>В C++ при вычислении по модулю $-1 \pmod N$ равно $N-1$.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Если $N$ простое, то для любого $a$ это условие обязательно выполняется. Напротив, если $N$ составное, математически доказано, что вероятность выполнения этого условия (вероятность обмана) при выборе случайного $a$ составляет не более $\frac{1}{4}$.
Если провести $k$ независимых тестов, вероятность ошибочной оценки становится меньше или равна $\left(\frac{1}{4}\right)^k$, что на практике можно считать нулем. Не существует чисел, способных «абсолютно обмануть» алгоритм, подобных числам Кармайкла.&lt;/p>
&lt;h2 id="поток-алгоритма-метода-миллера-рабина-блок-схема-mermaid">Поток алгоритма метода Миллера-Рабина (Блок-схема Mermaid)
&lt;/h2>&lt;p>Следующая схема показывает логический поток одного теста Миллера-Рабина (для одного основания $a$).&lt;/p>
&lt;div class="mermaid">graph TD
Start["Начало теста (Вход: N, a)"] --> CalcDS["Вычисление нечетного d и s, удовлетворяющих N-1 = d * 2^s"]
CalcDS --> CalcX["Вычисление x = a^d mod N"]
CalcX --> CheckX1{"x == 1 или x == N-1 ?"}
CheckX1 -- "Да" --> ReturnTrue["Возможно простое (Probably Prime)"]
CheckX1 -- "Нет" --> LoopStart["Начало цикла от r = 1 до s-1"]
LoopStart --> LoopCondition{"r &lt; s ?"}
LoopCondition -- "Нет" --> ReturnFalse["Точно составное (Composite)"]
LoopCondition -- "Да" --> SquareX["Вычисление x = (x * x) mod N"]
SquareX --> CheckXMinus1{"x == N - 1 ?"}
CheckXMinus1 -- "Да" --> ReturnTrue
CheckXMinus1 -- "Нет" --> CheckXOne{"x == 1 ?"}
CheckXOne -- "Да" --> ReturnFalse
CheckXOne -- "Нет" --> LoopNext["Увеличение r на 1 и переход к следующему"]
LoopNext --> LoopCondition&lt;/div>
&lt;hr>
&lt;h1 id="4-детерминированная-проверка-для-64-битных-целых-чисел">4. Детерминированная проверка для 64-битных целых чисел
&lt;/h1>&lt;p>Тест простоты Миллера-Рабина изначально является «вероятностным» алгоритмом, но если верхняя граница $N$ фиксирована, можно провести проверку со «100% достоверностью», протестировав все основания $a$ из определенного набора.
Это называется &lt;strong>Детерминированным тестом Миллера-Рабина (Deterministic Miller-Rabin Test)&lt;/strong>.&lt;/p>
&lt;p>Исследования Джима Синклера (Jim Sinclair) и других показали, что для всех целых чисел $N &lt; 2^{64}$ (около $1.8 \times 10^{19}$) достаточно выбрать и проверить следующие $7$ простых чисел в качестве оснований $a$, чтобы получить полностью детерминированный результат.&lt;/p>
&lt;p>&lt;strong>Список оснований $a$ для проверки:&lt;/strong>
&lt;code>{2, 325, 9375, 28178, 450775, 9780504, 1795265022}&lt;/code>&lt;/p>
&lt;p>Альтернативно, существует другой известный набор из $12$ простых чисел, с помощью которого также можно идеально проверить числа $N &lt; 2^{64}$.
&lt;code>{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}&lt;/code>&lt;/p>
&lt;p>В этот раз, чтобы повысить простоту и надежность алгоритма, мы применим подход с использованием этих $12$ простых чисел (или более оптимизированных $7$ баз). В реализации на C++ мы применим оптимизацию, минимизирующую количество тестов путем разделения диапазонов с помощью условных переходов.&lt;/p>
&lt;hr>
&lt;h1 id="5-высокоуровневая-реализация-на-c-highly-optimized-c-implementation">5. Высокоуровневая реализация на C++ (Highly Optimized C++ Implementation)
&lt;/h1>&lt;p>Теперь давайте объединим всю математическую теорию и дизайн алгоритма, чтобы представить реализацию функции проверки простоты Миллера-Рабина высочайшего уровня в современном C++.&lt;/p>
&lt;h2 id="ключевые-моменты-реализации">Ключевые моменты реализации
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Предотвращение переполнения при умножении 64-битных чисел:&lt;/strong>
При $N \approx 10^{18}$ значение $x \times x$ при умножении по модулю может достигать $10^{36}$, что с легкостью переполнит максимальное значение обычных 64-битных целых чисел (&lt;code>uint64_t&lt;/code> или &lt;code>long long&lt;/code>), равное $1.8 \times 10^{19}$.
Чтобы решить эту проблему, мы используем расширенный тип GCC или Clang &lt;code>__int128_t&lt;/code> (или &lt;code>unsigned __int128&lt;/code>) для выполнения вычислений с 128-битной точностью перед взятием модуля. Это позволяет быстро выполнять умножение по модулю без использования сложных алгоритмов.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Выбор детерминированных оснований:&lt;/strong>
Когда значение $N$ невелико, мы оптимизируем алгоритм так, чтобы требовалось протестировать лишь небольшое число оснований (bases).&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="полный-исходный-код-на-c">Полный исходный код на C++
&lt;/h2>&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;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;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&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;cstdint&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;initializer_list&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 с помощью 128-битного целого числа
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&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="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="p">)((&lt;/span>&lt;span class="kt">unsigned&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">b&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="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">// Вычисление (base^exp) 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">uint64_t&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&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">uint64_t&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">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">&amp;amp;&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_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&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="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&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">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">&amp;gt;&amp;gt;=&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="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">// Детерминированная проверка 64-битных целых чисел тестом Миллера-Рабина
&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">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">n&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">// Предварительная проверка граничных значений и малых простых чисел
&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">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&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">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">7&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">n&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="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">7&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="c1">// Разложение на вид n-1 = d * 2^s
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">n&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="kt">int&lt;/span> &lt;span class="n">s&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">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&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="n">d&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&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">s&lt;/span>&lt;span class="o">++&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">// Список оснований (bases) для тестирования
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Оптимизация для минимизации количества проверяемых оснований в зависимости от размера N
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">bases&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">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">4759123141ULL&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">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">61&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1122004669633ULL&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">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">23&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1662803&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">// 7 оснований, дающих детерминированный результат для всех чисел N &amp;lt; 2^64
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">325&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9375&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">28178&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">450775&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9780504&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1795265022&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="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">a&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">bases&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">a&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="k">if&lt;/span> &lt;span class="p">(&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 class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Если a кратно n, определить невозможно, но n не простое
&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="kt">uint64_t&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_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">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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&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">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&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="c1">// Цикл s-1 раз (x = x^2 mod n)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&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">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&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">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&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">x&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="k">if&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">n&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">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">false&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">break&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>&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">composite&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="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="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="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="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">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">test_cases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &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="mi">998244353&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="mi">1000000000000000003&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Простое число в районе 10^18
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Составное число (10^18 + 7)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">561&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="mi">18446744073709551557ULL&lt;/span> &lt;span class="c1">// Одно из наибольших простых чисел в районе 2^64
&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="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">n&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">test_cases&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">n&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s">&amp;#34;Prime&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s">&amp;#34;Composite&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="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>&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;hr>
&lt;h1 id="6-временная-сложность-алгоритма-и-оценка-производительности">6. Временная сложность алгоритма и оценка производительности
&lt;/h1>&lt;p>Рассмотрим производительность реализованного алгоритма.&lt;/p>
&lt;h2 id="временная-сложность-time-complexity">Временная сложность (Time Complexity)
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Метод пробных делений:&lt;/strong> $O(\sqrt{N})$&lt;/li>
&lt;li>&lt;strong>Тест Ферма:&lt;/strong> возведение в степень $O(\log N) \times k$ (где $k$ — количество попыток)&lt;/li>
&lt;li>&lt;strong>Метод Миллера-Рабина:&lt;/strong> возведение в степень и цикл $O(\log N) \times k$&lt;/li>
&lt;/ul>
&lt;p>В 64-битной среде ($N \le 2^{64}$) описанный выше детерминированный алгоритм Миллера-Рабина проверяет максимум $7$ оснований. Следовательно, $k \le 7$ можно считать константой, и общая временная сложность строго равна $O(\log N)$.
Даже в максимальном случае ($N \approx 10^{19}$) количество шагов выполнения укладывается в $7 \times 64 = 448$ базовых операций, а время выполнения составляет менее нескольких микросекунд ($10^{-6}$ секунд). По сравнению с методом пробных делений $O(\sqrt{N})$ (около $4 \times 10^9$ итераций цикла), достигается &lt;strong>ускорение в миллионы раз&lt;/strong>.&lt;/p>
&lt;h2 id="дальнейшая-оптимизация-умножение-монтгомери-montgomery-multiplication">Дальнейшая оптимизация: Умножение Монтгомери (Montgomery Multiplication)
&lt;/h2>&lt;p>В реализации, приведенной в этой статье, используется расширенный 128-битный тип &lt;code>__int128_t&lt;/code> для операции деления (вычисление остатка &lt;code>%&lt;/code>). Даже на современных процессорах целочисленное деление (инструкция DIV) — это дорогостоящая операция, занимающая десятки тактов по сравнению со сложением или умножением.&lt;/p>
&lt;p>Создатели библиотек и спортивные программисты, стремящиеся к экстремальной оптимизации, иногда применяют метод, называемый &lt;strong>Умножением Монтгомери (Montgomery Multiplication)&lt;/strong>. Умножение Монтгомери — это потрясающий алгоритм, который заменяет дорогостоящую операцию взятия модуля (деления) на «только битовые сдвиги и умножения» за счет отображения чисел в специальное «пространство Монтгомери».
Интегрировав этот метод в модульное умножение при тесте Миллера-Рабина, можно увеличить скорость выполнения еще примерно в 2-3 раза. Поскольку это очень глубокая тема, мы подробно рассмотрим её в другой статье.&lt;/p>
&lt;hr>
&lt;h1 id="7-заключение">7. Заключение
&lt;/h1>&lt;p>В этой статье мы охватили все аспекты проверки на простоту: от основ до продвинутых концепций.
Давайте вспомним ключевые моменты.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Метод пробных делений&lt;/strong> надежен, но из-за вычислительной сложности $O(\sqrt{N})$ он теряет практичность при $N > 10^{12}$.&lt;/li>
&lt;li>&lt;strong>Тест Ферма&lt;/strong> очень быстр — $O(\log N)$, но имеет фатальный недостаток: он ошибается на абсолютно псевдопростых числах, таких как числа Кармайкла.&lt;/li>
&lt;li>&lt;strong>Тест простоты Миллера-Рабина&lt;/strong> — это практичный и мощнейший алгоритм, который устраняет слабость теста Ферма.&lt;/li>
&lt;li>В реализации на C++ использование &lt;code>__int128_t&lt;/code> позволяет безопасно обрабатывать переполнения при умножении 64-битных целых чисел.&lt;/li>
&lt;li>В пределах 64-битных целых чисел ($N &lt; 2^{64}$), выбирая $7$ или $12$ определенных простых чисел в качестве оснований, возможна &lt;strong>детерминированная (со 100% точностью) проверка на простоту&lt;/strong>, а не вероятностная.&lt;/li>
&lt;/ol>
&lt;p>Быстрая проверка на простоту — это незаменимая технология при вычислениях с огромными числами. Предоставленный в этой статье исходный код алгоритма Миллера-Рабина на C++ достаточно надежен для использования в реальных задачах. Обязательно попробуйте применить его в своих проектах или на соревнованиях по алгоритмике!&lt;/p>
&lt;div class="mermaid">graph LR
TrialDivision["Метод пробных делений (O(√N))"] --> Fermat["Тест Ферма (O(log N), есть слабые места)"]
Fermat --> MillerRabin["Метод Миллера-Рабина (O(log N), можно сделать детерминированным)"]
MillerRabin --> Montgomery["+ Умножение Монтгомери (ускорение в несколько раз)"]
style MillerRabin fill:#f9f,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>Мир алгоритмов, в котором пересекаются программирование и математика, невероятно красив и глубок. Надеемся, что эта статья поможет вам в дальнейшем изучении.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Reference:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>Pomerance, C., Selfridge, J. L., &amp;amp; Wagstaff, S. S. (1980). The pseudoprimes to 25.10^9. Mathematics of Computation.&lt;/em>&lt;/li>
&lt;li>&lt;em>Sinclair, J. (2011). Deterministic Miller-Rabin primality testing.&lt;/em>&lt;/li>
&lt;/ul></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>Симуляция алгоритма Шора на Python</title><link>http://kenji.blog/ru/p/shors-algorithm-simulation-python/</link><pubDate>Fri, 11 Sep 2026 08:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/shors-algorithm-simulation-python/</guid><description>&lt;img src="http://kenji.blog/p/shors-algorithm-simulation-python/img/eyecatch.jpg" alt="Featured image of post Симуляция алгоритма Шора на Python" />&lt;h1 id="1-введение-кризис-криптографии-вызванный-квантовыми-компьютерами">1. Введение: Кризис криптографии, вызванный квантовыми компьютерами
&lt;/h1>&lt;p>Большая часть безопасности в современном интернет-обществе зависит от &lt;strong>криптографии с открытым ключом&lt;/strong> (в частности, шифрования RSA). Когда мы отправляем информацию о кредитной карте при онлайн-покупках или обмениваемся строго конфиденциальными данными, содержание этих коммуникаций надежно защищено шифрованием RSA.&lt;/p>
&lt;p>Безопасность шифрования RSA основана на математическом факте: «&lt;strong>факторизация огромных целых чисел крайне сложна для классических компьютеров (ПК и суперкомпьютеров, которые мы обычно используем)&lt;/strong>». Однако «&lt;strong>Алгоритм Шора (Shor&amp;rsquo;s Algorithm)&lt;/strong>», представленный Питером Шором (Peter Shor) в 1994 году, в корне разрушил эту предпосылку. Было математически доказано, что если выполнить алгоритм Шора на крупномасштабном квантовом компьютере, то факторизацию, которая заняла бы у классического компьютера больше времени, чем возраст Вселенной, можно решить всего за несколько минут или часов.&lt;/p>
&lt;p>В этой статье мы подробно и обстоятельно объясним, как алгоритм Шора выполняет факторизацию с такой высокой скоростью — от его математического устройства до конкретной реализации симуляции с использованием Python и фреймворка квантовых вычислений &lt;strong>Qiskit&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="2-радикальное-изменение-вычислительной-сложности-от-экспоненциального-к-полиномиальному-времени">2. Радикальное изменение вычислительной сложности: от экспоненциального к полиномиальному времени
&lt;/h1>&lt;p>Почему факторизация так сложна? Даже при использовании «общего метода решета числового поля (General Number Field Sieve, GNFS)», известного как лучший алгоритм факторизации для классических компьютеров, его вычислительная сложность является субэкспоненциальной.&lt;/p>
&lt;p>Временная сложность факторизации составного числа с количеством цифр $N$ с использованием классических методов выглядит следующим образом:&lt;/p>
$$ O\left(\exp\left( c (\log N)^{1/3} (\log \log N)^{2/3} \right)\right) $$
&lt;p>По этой причине, просто увеличив длину ключа (например, до 2048 или 4096 бит), дешифровка на классическом компьютере потребует нереалистичного времени — тысячи или десятки тысяч лет.&lt;/p>
&lt;p>Однако, если использовать &lt;strong>алгоритм Шора&lt;/strong> на квантовом компьютере, вычислительная сложность радикально сокращается до полиномиального времени относительно количества битов входных данных $\log N$.&lt;/p>
$$ O((\log N)^3) $$
&lt;p>Это означает, что при удвоении количества битов время вычислений на классическом компьютере возрастает астрономически, тогда как на квантовом компьютере оно увеличивается всего примерно в 8 раз. Это &lt;strong>сведение класса сложности от экспоненциального времени к полиномиальному (включение в класс BQP)&lt;/strong> и есть истинное величие алгоритма Шора.&lt;/p>
&lt;div class="mermaid">graph TD
A["Увеличение размера входа (количества бит) N"] --> B{"Выбор алгоритма"}
B -->|Классический: Общий метод решета числового поля| C["Субэкспоненциальный рост O(exp(...))"]
B -->|Квантовый: Алгоритм Шора| D["Полиномиальное время O((log N)^3)"]
C --> E["От тысяч до миллиардов лет (Невозможно взломать)"]
D --> F["От нескольких минут до часов (Взлом за реалистичное время)"]&lt;/div>
&lt;hr>
&lt;h1 id="3-общая-картина-алгоритма-и-математический-фундамент">3. Общая картина алгоритма и математический фундамент
&lt;/h1>&lt;p>Алгоритм Шора на самом деле выполняется не полностью на квантовом компьютере. Он состоит из взаимодействия предварительной и последующей обработки на классическом компьютере и ключевой части (алгоритма нахождения периода) на квантовом компьютере.&lt;/p>
&lt;p>Общий ход алгоритма выглядит следующим образом:&lt;/p>
&lt;div class="mermaid">graph TD
A["Вход: Составное число N для факторизации"] --> B["Выбор случайного числа a, где a &lt; N"]
B --> C{"gcd(a, N) > 1 ?"}
C -- "Да" --> D["Вывод тривиального делителя gcd(a, N) и завершение"]
C -- "Нет" --> E["Нахождение периода r функции f(x) = a^x mod N квантовым алгоритмом"]
E --> F{"r четное и a^(r/2) ≢ -1 mod N ?"}
F -- "Нет" --> B
F -- "Да" --> G["Вычисление делителей p = gcd(a^(r/2) - 1, N), q = gcd(a^(r/2) + 1, N)"]
G --> H["Выход: p, q"]&lt;/div>
&lt;h2 id="сведение-от-факторизации-к-проблеме-нахождения-периода">Сведение от факторизации к проблеме нахождения периода
&lt;/h2>&lt;p>Гениальное озарение Шора заключалось в преобразовании «&lt;strong>проблемы факторизации&lt;/strong>» в «&lt;strong>проблему нахождения порядка (Order Finding Problem)&lt;/strong>».&lt;/p>
&lt;p>Рассмотрим целое число $N$ (число, которое нужно факторизовать) и взаимно простое с ним целое число $a$ ($1 &lt; a &lt; N$). Определим следующую модулярную экспоненциальную функцию:&lt;/p>
$$ f(x) = a^x \bmod N $$
&lt;p>Эта функция имеет некоторый период $r$. То есть для любого $x$ выполняется $f(x+r) = f(x)$. В частности, при $x=0$:&lt;/p>
$$ a^r \equiv 1 \pmod N $$
&lt;p>Минимальное положительное целое число $r$, при котором это выполняется, называется «порядком (Order) $a$ по модулю $N$». Если мы сможем найти этот период $r$, то сможем вывести простые множители следующим образом.&lt;/p>
&lt;p>Преобразовав уравнение, получим:
&lt;/p>
$$ a^r - 1 \equiv 0 \pmod N $$
&lt;p>
Если $r$ четное, мы можем разложить его на множители, используя формулу разности квадратов:
&lt;/p>
$$ (a^{r/2} - 1)(a^{r/2} + 1) \equiv 0 \pmod N $$
&lt;p>Это означает, что $N$ имеет общий делитель либо с $(a^{r/2} - 1)$, либо с $(a^{r/2} + 1)$ (однако должно выполняться условие $a^{r/2} \not\equiv -1 \pmod N$). Следовательно, используя алгоритм Евклида:&lt;/p>
$$ p = \gcd(a^{r/2} - 1, N) $$
$$ q = \gcd(a^{r/2} + 1, N) $$
&lt;p>Вычислив это, мы можем найти нетривиальные простые множители $p, q$ числа $N$. Эти вычисления (нахождение наибольшего общего делителя и генерация случайных чисел) выполняются очень быстро на классическом компьютере. Проблема сводится к тому, &lt;strong>как быстро найти период $r$&lt;/strong>. На классическом компьютере поиск самого этого периода $r$ занимает экспоненциальное время. И здесь на сцену выходит квантовый компьютер.&lt;/p>
&lt;hr>
&lt;h1 id="4-часть-квантового-алгоритма-механизм-нахождения-периода">4. Часть квантового алгоритма: Механизм нахождения периода
&lt;/h1>&lt;p>Подпрограмма для нахождения периода $r$ с использованием квантового компьютера состоит из следующих 4 шагов:&lt;/p>
&lt;div class="mermaid">graph LR
subgraph "Переходы квантовых состояний"
S1["|0⟩|0⟩ (Инициализация)"] --> S2["Вентиль H: Суперпозиция Σ|x⟩|0⟩"]
S2 --> S3["Оракул U: Σ|x⟩|a^x mod N⟩"]
S3 --> S4["QFT: Извлечение периода через интерференцию"]
S4 --> S5["Измерение: Получение приближенного значения y"]
end&lt;/div>
&lt;h2 id="шаг-1-инициализация-квантового-регистра-и-суперпозиция">Шаг 1: Инициализация квантового регистра и суперпозиция
&lt;/h2>&lt;p>Сначала мы подготавливаем два квантовых регистра. Первый регистр предназначен для ввода состояния, а второй — для сохранения результата вычислений функции.
Начальное состояние полностью равно $|0\rangle$.&lt;/p>
$$ |\psi_0\rangle = |0\rangle_1 |0\rangle_2 $$
&lt;p>Применив вентиль Адамара (Hadamard Gate) ко всем кубитам первого регистра, мы создаем состояние равновероятной суперпозиции всех возможных входов $x$ (от $0$ до $Q-1$, где $Q=2^n$).&lt;/p>
$$ |\psi_1\rangle = \frac{1}{\sqrt{Q}} \sum_{x=0}^{Q-1} |x\rangle_1 |0\rangle_2 $$
&lt;p>Благодаря этому квантовый компьютер одновременно сохраняет состояния для всех $Q$ входов за одну операцию. Это является мощным источником &lt;strong>квантового параллелизма&lt;/strong>.&lt;/p>
&lt;h2 id="шаг-2-применение-функции-оракула-модульное-возведение-в-степень">Шаг 2: Применение функции-оракула (модульное возведение в степень)
&lt;/h2>&lt;p>Далее, используя квантовую схему $U_f$, мы вычисляем функцию $f(x) = a^x \bmod N$ и сохраняем ее результат во втором регистре.&lt;/p>
$$ |\psi_2\rangle = \frac{1}{\sqrt{Q}} \sum_{x=0}^{Q-1} |x\rangle_1 |a^x \bmod N\rangle_2 $$
&lt;p>На этом этапе первый и второй регистры находятся в состоянии &lt;strong>квантовой запутанности (энтанглмента)&lt;/strong>. Если бы мы (гипотетически) измерили второй регистр и получили определенное значение $k = a^{x_0} \bmod N$, состояние первого регистра коллапсировало бы в суперпозицию тех $x$, которые дают это значение $k$. Поскольку период функции равен $r$, оставшиеся состояния будут иметь значения с шагом $r$: $x_0, x_0+r, x_0+2r, \dots$&lt;/p>
$$ |\psi_3\rangle = \sqrt{\frac{r}{Q}} \sum_{j=0}^{M-1} |x_0 + j r\rangle_1 |k\rangle_2 $$
&lt;p>Однако мы хотим узнать не $x_0$, а сам период $r$. Напрямую измерить $r$ из этого состояния невозможно. Поэтому мы используем квантовое преобразование Фурье.&lt;/p>
&lt;h2 id="шаг-3-фазовая-интерференция-через-квантовое-преобразование-фурье-qft">Шаг 3: Фазовая интерференция через Квантовое преобразование Фурье (QFT)
&lt;/h2>&lt;p>Мы применяем &lt;strong>Квантовое преобразование Фурье (Quantum Fourier Transform, QFT)&lt;/strong> к первому регистру. QFT — это квантовая версия классического дискретного преобразования Фурье, которая преобразует амплитуды вектора состояния. Действие QFT на базисное состояние $|x\rangle$ определяется следующим образом:&lt;/p>
$$ QFT |x\rangle = \frac{1}{\sqrt{Q}} \sum_{y=0}^{Q-1} \omega^{xy} |y\rangle $$
&lt;p>Где $\omega = e^{2\pi i / Q}$.&lt;/p>
&lt;p>Применение QFT вызывает интерференцию амплитуд состояний. Опуская математические подробности: если применить QFT к состоянию с периодом $r$, волны будут создавать &lt;strong>конструктивную интерференцию (Constructive Interference)&lt;/strong> только тогда, когда $y$ очень близко к кратному $Q/r$. Для всех остальных состояний из-за &lt;strong>деструктивной интерференции (Destructive Interference)&lt;/strong> амплитуды вероятностей взаимно уничтожаются, приближаясь к нулю.&lt;/p>
&lt;h2 id="шаг-4-измерение-и-разложение-в-непрерывную-дробь">Шаг 4: Измерение и разложение в непрерывную дробь
&lt;/h2>&lt;p>Наконец, мы измеряем первый регистр. Полученное при измерении значение $y$ с высокой вероятностью удовлетворяет следующему условию:&lt;/p>
$$ y \approx c \frac{Q}{r} \implies \frac{y}{Q} \approx \frac{c}{r} $$
&lt;p>(Где $c$ — неизвестное целое число $0 \le c &lt; r$)&lt;/p>
&lt;p>Применив классический алгоритм &lt;strong>разложения в непрерывную дробь (Continued Fraction Expansion)&lt;/strong> к полученному рациональному числу $y/Q$, мы вычисляем подходящую дробь $c/r$ и извлекаем период $r$ из ее знаменателя.&lt;/p>
&lt;hr>
&lt;h1 id="5-реализация-симуляции-с-использованием-python-и-qiskit">5. Реализация симуляции с использованием Python и Qiskit
&lt;/h1>&lt;p>Поскольку одна лишь теория не дает полного представления, давайте попробуем смоделировать алгоритм Шора на практике, используя Python и фреймворк квантовых вычислений от IBM — &lt;strong>Qiskit&lt;/strong>.&lt;/p>
&lt;p>Здесь мы реализуем самый классический и известный пример: &lt;strong>«факторизация $N=15$ с использованием $a=7$»&lt;/strong>.&lt;/p>
&lt;h2 id="подготовка-среды-выполнения">Подготовка среды выполнения
&lt;/h2>&lt;p>Пожалуйста, заранее установите Qiskit.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install qiskit qiskit-aer numpy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="полный-код-реализации-на-python">Полный код реализации на Python
&lt;/h2>&lt;p>Следующий код является примером реализации алгоритма Шора, адаптированного для $N=15, a=7$. Поскольку построение универсальной модульной экспоненциальной схемы требует слишком высоких вычислительных затрат на текущих симуляторах, операции вентилей для конкретного случая $a=7$ здесь жестко закодированы.&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;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&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">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 class="kn">from&lt;/span> &lt;span class="nn">qiskit&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit_aer&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AerSimulator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit.visualization&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">plot_histogram&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fractions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Fraction&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">math&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. Функция для построения обратного квантового преобразования Фурье (QFT†)&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">qft_dagger&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="s2">&amp;#34;&amp;#34;&amp;#34;Генерация схемы обратного квантового преобразования Фурье для n кубитов&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&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="c1"># Вентили SWAP для обращения порядка&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">qubit&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">n&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="n">qc&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">qubit&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">qubit&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="c1"># Применение управляемых фазовых вентилей и вентиля H&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">j&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">n&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="n">m&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">j&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cp&lt;/span>&lt;span class="p">(&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">pi&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nb">float&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">m&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">j&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;QFT_dagger&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="n">qc&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. Функция для построения управляемой модульной экспоненциальной операции 7^x mod 15&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">c_amod15&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">power&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;Генерация управляемого вентиля U для конкретного a и степени (только для N=15)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&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="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">power&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Жестко закодированная логика 7^x mod 15 для случая a=7&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="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&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="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&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">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&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="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&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">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&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="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">swap&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">2&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="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">13&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="n">q&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="mi">4&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&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">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">U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to_gate&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&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">a&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">^&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">power&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> mod 15&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c_U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">U&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">control&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">c_U&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="k">def&lt;/span> &lt;span class="nf">shor_circuit&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">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># n_count: Количество битов управляющего регистра&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Целевой регистр использует 4 бита для представления чисел от 0 до 15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QuantumCircuit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_count&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">for&lt;/span> &lt;span class="n">q&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">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">(&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Инициализация второго регистра (целевого регистра) в |1&amp;gt; (0001)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&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">3&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">n_count&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">for&lt;/span> &lt;span class="n">q&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">n_count&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Применение операции в степени 2^q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c_amod15&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&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">q&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="n">q&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="n">n_count&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">i&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="mi">4&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">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qft_dagger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&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">qc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">measure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_count&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">qc&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="s2">&amp;#34;__main__&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">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">15&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="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span> &lt;span class="c1"># Использование 8 кубитов для управляющего регистра (Q=256)&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">, a=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, количество управляющих кубитов=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">n_count&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">qc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">shor_circuit&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">n_count&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">sim&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AerSimulator&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># В новых версиях Qiskit рекомендуется использовать transpile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">qiskit&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">transpile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compiled_circuit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">transpile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">qc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sim&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">job&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sim&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">compiled_circuit&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">shots&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&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">job&lt;/span>&lt;span class="o">.&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="n">counts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_counts&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="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&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">for&lt;/span> &lt;span class="n">bitstring&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">count&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">counts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&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">bitstring&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">count&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"># Классическая постобработка: Определение периода r с помощью разложения в непрерывную дробь&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;&lt;/span>&lt;span class="se">\n&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="n">phases&lt;/span> &lt;span class="o">=&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="n">output&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">counts&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">decimal&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">,&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"># Фаза = Измеренное значение / 2^n_count&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">decimal&lt;/span> &lt;span class="o">/&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">n_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phases&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">phase&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"># Получение подходящей дроби через разложение в непрерывную дробь. Верхний предел знаменателя - N=15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">frac&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Fraction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">limit_denominator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">15&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">frac&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">denominator&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">decimal&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">3d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | Фаза: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">phase&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.4f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | Непрерывная дробь: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">frac&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | Оценочный период r = &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">r&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"># Проверка, является ли период r четным и дает ли он действительный результат&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">r&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="n">guess1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">math&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">a&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&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">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">guess2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">math&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">a&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="mi">2&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">N&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">guess1&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&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">N&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">guess2&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&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">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; =&amp;gt; Успех! Простые множители &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"> - это &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">guess1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> и &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">guess2&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; =&amp;gt; Только тривиальные множители. Повторить.&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; =&amp;gt; Неудача, так как период нечетный.&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;h2 id="объяснение-кода-и-анализ-результатов-выполнения">Объяснение кода и анализ результатов выполнения
&lt;/h2>&lt;p>При выполнении приведенного выше кода в качестве результатов измерения управляющего регистра с высокой вероятностью будут получены определенные пики (наблюдаемые значения). При &lt;code>n_count=8&lt;/code> ($Q=256$) на идеальном квантовом компьютере (или симуляторе) такие значения, как &lt;code>0&lt;/code>, &lt;code>64&lt;/code>, &lt;code>128&lt;/code>, &lt;code>192&lt;/code>, появятся с подавляющей вероятностью.&lt;/p>
&lt;p>Если разделить их на $Q=256$, то фаза $y/Q$ будет соответственно $0.0$, $0.25$, $0.5$, $0.75$.
Если разложить эту фазу в непрерывную дробь:&lt;/p>
&lt;ul>
&lt;li>$0.25 \to 1/4$ (Оценочный период $r=4$)&lt;/li>
&lt;li>$0.50 \to 1/2$ (Оценочный период $r=2$)&lt;/li>
&lt;li>$0.75 \to 3/4$ (Оценочный период $r=4$)&lt;/li>
&lt;/ul>
&lt;p>Используя полученный период $r=4$, мы вычисляем простые множители.
Поскольку $a=7, r=4$:
$p = \gcd(7^2 - 1, 15) = \gcd(48, 15) = 3$
$q = \gcd(7^2 + 1, 15) = \gcd(50, 15) = 5$&lt;/p>
&lt;p>Мы успешно выполнили факторизацию $15 = 3 \times 5$.&lt;/p>
&lt;blockquote>
&lt;p>[!TIP]
Если получено измеренное значение $y=128$ (фаза $0.5$), знаменатель будет $2$, и мы получим делитель периода, а не истинный период $r=4$. В таких случаях можно найти истинный период, выполнив алгоритм несколько раз или проверив кратные полученного $r$.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h1 id="6-проблемы-практического-применения-и-ограничения-эпохи-nisq">6. Проблемы практического применения и ограничения эпохи NISQ
&lt;/h1>&lt;p>Хотя на симуляторе легко факторизовать $N=15$, для факторизации RSA-2048 (617-значное десятичное число), используемого в реальном мире, перед реальными квантовыми компьютерами всё ещё стоит множество барьеров.&lt;/p>
&lt;p>Эпоха, в которой мы живем сейчас, называется &lt;strong>эпохой NISQ (Noisy Intermediate-Scale Quantum: шумные квантовые устройства промежуточного масштаба)&lt;/strong>. Кубиты крайне уязвимы к шуму из внешней среды, и в процессе вычислений происходит «декогеренция», разрушающая их состояние.&lt;/p>
&lt;p>Для точного выполнения глубоких схем (с большим количеством вентилей), таких как алгоритм Шора, абсолютно необходима &lt;strong>квантовая коррекция ошибок (Quantum Error Correction)&lt;/strong>. Для создания одного «логического кубита» без шумов необходимо закодировать тысячи «физических кубитов» с помощью поверхностных кодов (Surface Code) и других методов.&lt;/p>
&lt;p>По оценкам, для взлома 2048-битного шифрования RSA потребуются тысячи идеальных логических кубитов, а для их реализации потребуется отказоустойчивый квантовый компьютер, оснащенный &lt;strong>от нескольких миллионов до десятков миллионов физических кубитов&lt;/strong>. Поскольку даже современные передовые квантовые процессоры имеют лишь от сотен до нескольких тысяч физических кубитов, шифрование по всему миру не будет взломано в одночасье.&lt;/p>
&lt;blockquote>
&lt;p>[!WARNING]
Однако существует модель угрозы «Store Now, Decrypt Later (Сохранить сейчас, расшифровать позже)». Злоумышленники могут использовать стратегию массового сохранения текущих зашифрованных конфиденциальных коммуникаций в виде зашифрованных данных, чтобы полностью расшифровать их в тот самый момент, когда через 10–20 лет появятся мощные квантовые компьютеры.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h1 id="7-переход-к-постквантовой-криптографии-pqc">7. Переход к постквантовой криптографии (PQC)
&lt;/h1>&lt;p>В преддверии такого «Q-Day» (дня, когда квантовые компьютеры взломают шифрование), криптографы по всему миру во главе с Национальным институтом стандартов и технологий США (NIST) работают над стандартизацией &lt;strong>постквантовой криптографии (Post-Quantum Cryptography, PQC)&lt;/strong>.&lt;/p>
&lt;p>PQC основывается на новых математических проблемах (например, проблемы на решетках, многомерные полиномиальные проблемы, алгоритмы на основе хеш-функций), которые, как считается математически, не могут быть эффективно решены даже с помощью алгоритма Шора (или алгоритма Гровера). Такие алгоритмы, как «CRYSTALS-Kyber» и «CRYSTALS-Dilithium», уже выбраны в качестве стандартов, и постепенно начинается их внедрение в iMessage от Apple и коммуникационные протоколы различных веб-браузеров.&lt;/p>
&lt;p>Для инженеров, управляющих ИТ-инфраструктурой, одной из главных задач в будущем станет внедрение в системы «криптографической гибкости (crypto-agility: возможности быстрого переключения методов шифрования)» для перехода от существующего RSA и криптографии на эллиптических кривых к PQC.&lt;/p>
&lt;hr>
&lt;h1 id="8-заключение">8. Заключение
&lt;/h1>&lt;p>В этой статье мы провели исчерпывающее объяснение объемом в 10 тысяч символов: начиная с теоретического математического фундамента алгоритма Шора, механизма извлечения периода с использованием квантового преобразования Фурье, и заканчивая конкретным кодом симуляции на Python и Qiskit.&lt;/p>
&lt;p>Тот факт, что законы физики микромира — квантовой механики — могут фундаментально разрушить теорию вычислительной сложности и криптографию, которые являются основой макроскопической информатики, представляет собой один из самых захватывающих сдвигов парадигмы в истории науки. Мы продолжим внимательно следить за разворачивающейся борьбой между постоянно развивающимися технологиями квантовых вычислений и противостоящими им новыми технологиями шифрования.&lt;/p>
&lt;p>Обязательно запустите представленный здесь код на Python в вашей собственной среде и испытайте на себе «магию вычислений», создаваемую суперпозицией и интерференцией квантовых состояний.&lt;/p>
&lt;hr>
&lt;p>&lt;strong>Источники и литература&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Shor, P. W. (1994). &amp;ldquo;Algorithms for quantum computation: discrete logarithms and factoring&amp;rdquo;. Proceedings 35th Annual Symposium on Foundations of Computer Science.&lt;/li>
&lt;li>Nielsen, M. A., &amp;amp; Chuang, I. L. (2010). &amp;ldquo;Quantum Computation and Quantum Information&amp;rdquo;. Cambridge University Press.&lt;/li>
&lt;li>Qiskit Documentation: &lt;a class="link" href="https://qiskit.org/documentation/" target="_blank" rel="noopener"
>https://qiskit.org/documentation/&lt;/a>&lt;/li>
&lt;/ul></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><item><title>День, когда квантовые компьютеры станут практичными: текущее состояние в 2026 году</title><link>http://kenji.blog/ru/p/quantum-computing-2026-current-status/</link><pubDate>Fri, 11 Sep 2026 06:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/quantum-computing-2026-current-status/</guid><description>&lt;img src="http://kenji.blog/p/quantum-computing-2026-current-status/img/eyecatch.jpg" alt="Featured image of post День, когда квантовые компьютеры станут практичными: текущее состояние в 2026 году" />&lt;h2 id="1-введение-насколько-продвинулись-квантовые-компьютеры-к-2026-году">1. Введение: Насколько продвинулись квантовые компьютеры к 2026 году
&lt;/h2>&lt;p>По состоянию на 2026 год квантовые вычисления совершили решающий переход от &amp;ldquo;теоретической мечты&amp;rdquo; к &amp;ldquo;инженерной реальности&amp;rdquo;. По мере того, как ограничения устройств &lt;strong>NISQ (Noisy Intermediate-Scale Quantum)&lt;/strong>, которые были мейнстримом еще несколько лет назад, стали очевидны, исследовательские институты и технологические гиганты по всему миру взяли курс на реализацию &amp;ldquo;отказоустойчивых квантовых вычислений (FTQC: Fault-Tolerant Quantum Computing)&amp;rdquo;.&lt;/p>
&lt;p>В этой статье мы глубоко погрузимся в текущее состояние квантовых компьютеров с учетом последних прорывов 2026 года. В частности, мы подробно рассмотрим квантовую коррекцию ошибок (поверхностный код), разницу между физическими и логическими кубитами, прогресс в топологических квантовых вычислениях, а также передовые достижения в сверхпроводящих и ионных ловушках.&lt;/p>
&lt;hr>
&lt;h2 id="2-основы-квантовых-состояний-и-точность-fidelity">2. Основы квантовых состояний и точность (Fidelity)
&lt;/h2>&lt;p>Кубит (Qubit), базовая единица квантового компьютера, в отличие от классического бита (0 или 1), может находиться в состоянии суперпозиции 0 и 1 (Superposition). Состояние одиночного кубита представляется в виде вектора в гильбертовом пространстве следующим образом:&lt;/p>
$$
|\psi\rangle = \alpha|0\rangle + \beta|1\rangle
$$
&lt;p>Где $\alpha$ и $\beta$ — комплексные амплитуды вероятности, удовлетворяющие следующему условию нормировки:&lt;/p>
$$
|\alpha|^2 + |\beta|^2 = 1
$$
&lt;p>Чрезвычайно важным показателем при оценке производительности квантовых вычислений является &lt;strong>точность (Fidelity)&lt;/strong>. Точность $F$ между идеальным квантовым состоянием $|\psi\rangle$ и фактической матрицей плотности $\rho$, которая деградировала из-за шума и стала смешанным состоянием, определяется следующим образом:&lt;/p>
$$
F(\rho, |\psi\rangle) = \langle \psi | \rho | \psi \rangle
$$
&lt;p>В 2026 году точность двухкубитных вентилей (например, вентилей CNOT и CZ) в сверхпроводящих системах стала стабильно превышать барьер в &lt;strong>99,99%&lt;/strong> (так называемые &amp;ldquo;четыре девятки&amp;rdquo;). Это значение значительно превышает порог коррекции ошибок поверхностного кода (около 99%) и является одним из крупнейших прорывов на пути к практическому применению.&lt;/p>
&lt;hr>
&lt;h2 id="3-пределы-эры-nisq-и-смена-парадигмы-к-ftqc">3. Пределы эры NISQ и смена парадигмы к FTQC
&lt;/h2>&lt;p>Период со второй половины 2010-х до первой половины 2020-х годов был эрой NISQ (Noisy Intermediate-Scale Quantum) — устройств от нескольких десятков до сотен кубитов без коррекции ошибок. Однако у NISQ были четкие ограничения.&lt;/p>
&lt;p>По мере увеличения глубины схемы (Depth) ошибки накапливаются экспоненциально, что делает невозможным получение значимых результатов вычислений. Общая вероятность успеха $P_{success}$ при глубине схемы $D$ затухает относительно точности одиночного вентиля $f$ и количества вентилей $N$ следующим образом:&lt;/p>
$$
P_{success} \approx f^N
$$
&lt;p>Если $f = 0.99$ и применяется 1000 вентилей, то $0.99^{1000} \approx 4.3 \times 10^{-5}$, и результат почти полностью теряется в случайном шуме. По этой причине в 2026 году ресурсы сосредоточены на создании &lt;strong>логических кубитов (Logical Qubit)&lt;/strong>, а не на прямом масштабировании алгоритмов NISQ (таких как VQE или QAOA).&lt;/p>
&lt;hr>
&lt;h2 id="4-квантовая-коррекция-ошибок-и-логические-кубиты-передовая-линия-поверхностного-кода">4. Квантовая коррекция ошибок и логические кубиты: передовая линия поверхностного кода
&lt;/h2>&lt;p>Квантовая коррекция ошибок (QEC: Quantum Error Correction) — это технология, которая кодирует несколько &amp;ldquo;физических кубитов&amp;rdquo; для создания одного &amp;ldquo;логического кубита&amp;rdquo;, обнаруживая и исправляя ошибки. В настоящее время наиболее перспективным считается &lt;strong>поверхностный код (Surface Code)&lt;/strong>.&lt;/p>
&lt;h3 id="41-структура-поверхностного-кода-surface-code">4.1 Структура поверхностного кода (Surface Code)
&lt;/h3>&lt;p>В поверхностном коде кубиты располагаются в виде двумерной решетки. Кубиты данных (хранящие фактическую информацию) и кубиты измерения (для измерения синдрома) располагаются в шахматном порядке.&lt;/p>
&lt;div class="mermaid">graph TD
A["Кубит данных (D1)"] --- B["Кубит измерения (M1)"]
B --- C["Кубит данных (D2)"]
C --- D["Кубит измерения (M2)"]
D --- E["Кубит данных (D3)"]
B --- F["Кубит данных (D4)"]
D --- G["Кубит данных (D5)"]
style A fill:#e1f5fe,stroke:#039be5
style C fill:#e1f5fe,stroke:#039be5
style E fill:#e1f5fe,stroke:#039be5
style F fill:#e1f5fe,stroke:#039be5
style G fill:#e1f5fe,stroke:#039be5
style B fill:#fff3e0,stroke:#fb8c00
style D fill:#fff3e0,stroke:#fb8c00&lt;/div>
&lt;p>Используя операторы стабилизатора $S_x$ и $S_z$, постоянно отслеживаются перевороты битов (X-ошибки) и перевороты фазы (Z-ошибки).&lt;/p>
$$
S_x = \prod_{i \in \text{star}} X_i, \quad S_z = \prod_{j \in \text{plaquette}} Z_j
$$
&lt;p>Важным достижением 2026 года стало полное преодоление &amp;ldquo;точки безубыточности (Break-even point)&amp;rdquo;. Иными словами, шум, устраняемый коррекцией ошибок, стал больше, чем шум, вызываемый дополнительными схемами коррекции, и срок жизни логических кубитов стал на несколько порядков превышать срок жизни физических кубитов.&lt;/p>
&lt;h3 id="42-цикл-квантовой-коррекции-ошибок">4.2 Цикл квантовой коррекции ошибок
&lt;/h3>&lt;p>Коррекция ошибок работает как непрерывный цикл обратной связи.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant D as "Кубиты данных"
participant M as "Вспомогательные/Измерительные кубиты"
participant C as "Классический контроллер"
loop "Цикл извлечения синдрома (около 1 микросекунды)"
D->>M: "Запутывание (CNOT/CZ)"
M->>C: "Измерение состояния (Синдром)"
C->>C: "Декодирование синдрома (например, Minimum Weight Perfect Matching)"
C-->>D: "Применение коррекции Паули (при необходимости)"
end&lt;/div>
&lt;p>В настоящее время создана технология, позволяющая выполнять это классическое декодирование (анализ синдрома) на FPGA или специализированных ASIC за наносекунды, и коррекция ошибок в реальном времени перешла на стадию практического применения.&lt;/p>
&lt;hr>
&lt;h2 id="5-эволюция-аппаратной-архитектуры-версия-2026-года">5. Эволюция аппаратной архитектуры (Версия 2026 года)
&lt;/h2>&lt;p>В 2026 году квантовое аппаратное обеспечение развивается в основном по трем направлениям: &amp;ldquo;сверхпроводящие системы&amp;rdquo;, &amp;ldquo;ионные ловушки&amp;rdquo; и &amp;ldquo;топологические системы&amp;rdquo;.&lt;/p>
&lt;h3 id="51-интеграция-сверхпроводящих-кубитов">5.1 Интеграция сверхпроводящих кубитов
&lt;/h3>&lt;p>Сверхпроводящие системы — область, в которой лидируют IBM и Google; в ней доминируют трансмонные (Transmon) кубиты, использующие джозефсоновские переходы. К 2026 году были созданы мегачипы, интегрирующие от нескольких тысяч до десяти тысяч физических кубитов на одном кристалле.&lt;/p>
&lt;p>Особо следует отметить создание &lt;strong>межмодульных квантовых коммуникаций (Quantum Interconnects)&lt;/strong>. Квантовая телепортация между чипами с использованием микроволновых фотонов была реализована на коммерческом уровне, что позволило обойти ограничения по размеру одного рефрижератора растворения.&lt;/p>
&lt;h3 id="52-двумерное-масштабирование-ионных-ловушек-и-оптические-межсоединения">5.2 Двумерное масштабирование ионных ловушек и оптические межсоединения
&lt;/h3>&lt;p>Системы ионных ловушек (возглавляемые Quantinuum, IonQ и др.) используют внутренние энергетические состояния ионов, левитирующих в вакууме, в качестве кубитов. По сравнению со сверхпроводящими системами их преимущество заключается в чрезвычайно длительном времени когерентности T1/T2 и возможности связи &amp;ldquo;каждый с каждым&amp;rdquo; (All-to-All Connectivity).&lt;/p>
&lt;p>Прорывом 2026 года стал переход архитектуры QCCD (Quantum Charge Coupled Device) в двумерный формат и высокоскоростная генерация запутанности между несколькими ловушками с использованием фотонных межсоединений. Это кардинально улучшило &amp;ldquo;низкую скорость вентилей&amp;rdquo; и &amp;ldquo;масштабируемость&amp;rdquo;, которые были слабыми местами ионных ловушек.&lt;/p>
&lt;h3 id="53-топологические-квантовые-вычисления-контроль-энионов">5.3 Топологические квантовые вычисления: контроль энионов
&lt;/h3>&lt;p>&lt;strong>Топологические квантовые вычисления&lt;/strong>, долгое время считавшиеся теоретическими, в 2026 году наконец перешли на стадию экспериментальной проверки. Этот метод, продвигаемый Microsoft и другими, использует неабелевы энионы (Non-Abelian Anyons), называемые &amp;ldquo;нулевыми модами Майораны (Majorana Zero Modes)&amp;rdquo;.&lt;/p>
&lt;p>Квантовые вентили выполняются с помощью операции &amp;ldquo;плетения (Braiding)&amp;rdquo;, которая меняет местами позиции энионных частиц.&lt;/p>
$$
|\psi_{final}\rangle = B_{ij} |\psi_{initial}\rangle
$$
&lt;p>Где $B_{ij}$ — оператор плетения. Топологический подход по своей сути устойчив к шуму окружающей среды (аппаратный уровень отказоустойчивости), поскольку хранение информации зависит от топологии всего &amp;ldquo;узла&amp;rdquo;, а не от локального состояния частиц. В 2026 году впервые в мире было подтверждено создание топологических логических кубитов с высокой точностью, что привлекло внимание как мощный короткий путь к FTQC.&lt;/p>
&lt;hr>
&lt;h2 id="6-дорожная-карта-и-перспективы-практического-применения">6. Дорожная карта и перспективы практического применения
&lt;/h2>&lt;p>Чтобы квантовые компьютеры могли по-настоящему продемонстрировать &lt;strong>квантовое превосходство (Quantum Advantage)&lt;/strong>, превосходя классические компьютеры (суперкомпьютеры) в таких областях, как &amp;ldquo;вычислительная химия&amp;rdquo;, &amp;ldquo;материаловедение&amp;rdquo; и &amp;ldquo;финансовое моделирование&amp;rdquo;, требуются тысячи логических кубитов.&lt;/p>
&lt;div class="mermaid">gantt
title "Дорожная карта квантовых вычислений (пересмотрена в 2026 году)"
dateFormat YYYY
axisFormat %Y
section "Эра NISQ"
"Шумные кубиты (&lt;1000)" :done, 2018, 2024
section "Ранний FTQC"
"Демонстрация точки безубыточности" :done, 2024, 2026
"Сотни логических кубитов" :active, 2026, 2028
section "Полномасштабный FTQC"
"1000+ логических кубитов (Коммерческое применение)" : 2028, 2030
"Универсальный отказоустойчивый квантовый компьютер" : 2030, 2035&lt;/div>
&lt;h3 id="61-текущие-проблемы-и-будущее">6.1 Текущие проблемы и будущее
&lt;/h3>&lt;p>Крупнейшими проблемами по состоянию на 2026 год являются охлаждающая способность гигантских криостатов (рефрижераторов растворения) для поддержания сверхнизких температур, а также проводка (узкое место ввода/вывода), соединяющая устройства управления при комнатной температуре и криогенные квантовые чипы. В ответ на это ускоренными темпами разрабатываются чипы контроллеров CMOS (Cryo-CMOS), работающие в криогенных условиях.&lt;/p>
&lt;h3 id="заключение">Заключение
&lt;/h3>&lt;p>2026 год войдет в историю квантовых компьютеров как &amp;ldquo;первый год масштабирования логических кубитов&amp;rdquo;. Благодаря демонстрации алгоритмов коррекции ошибок, модуляризации аппаратного обеспечения и стремительному прогрессу топологического подхода, &amp;ldquo;день практического применения&amp;rdquo; больше не является далеким будущим, а стал конкретным рубежом ближайших нескольких лет. Для разработчиков квантовых алгоритмов и компаний сейчас самое время инвестировать в решение задач с помощью квантовых технологий.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Эта статья написана на основе новейших исследовательских работ и отраслевых тенденций в области квантовых вычислений по состоянию на 2026 год.&lt;/em>&lt;/p></description></item></channel></rss>