<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cryptography on kenji.blog</title><link>http://kenji.blog/ru/tags/cryptography/</link><description>Recent content in Cryptography on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 21:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/tags/cryptography/index.xml" rel="self" type="application/rss+xml"/><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>История криптографии: от шифра Цезаря до постквантовой криптографии (PQC)</title><link>http://kenji.blog/ru/p/history-of-cryptography-caesar-to-pqc/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/history-of-cryptography-caesar-to-pqc/</guid><description>&lt;img src="http://kenji.blog/p/history-of-cryptography-caesar-to-pqc/img/eyecatch.jpg" alt="Featured image of post История криптографии: от шифра Цезаря до постквантовой криптографии (PQC)" />&lt;h1 id="1-введение-что-такое-криптография">1. Введение: Что такое криптография?
&lt;/h1>&lt;p>Криптография (Cryptography) — это технология сохранения конфиденциальности информации, которая развивалась вместе с историей человечества. От передачи секретных приказов на древних войнах до защиты данных кредитных карт в современном интернете, цель криптографии остается неизменной: &amp;ldquo;Сделать так, чтобы только предполагаемый получатель мог понять информацию, и чтобы третья сторона не смогла ее расшифровать&amp;rdquo;.&lt;/p>
&lt;p>В современной информационной безопасности криптография играет важную роль, не ограничиваясь простой &amp;ldquo;секретностью информации (Конфиденциальность: Confidentiality)&amp;rdquo;, но и обеспечивая &amp;ldquo;целостность (Integrity)&amp;rdquo;, &amp;ldquo;аутентификацию (Authentication)&amp;rdquo; и &amp;ldquo;неотрекаемость (Non-repudiation)&amp;rdquo; данных.&lt;/p>
&lt;p>В этой статье мы подробно рассмотрим историю развития криптографии с технологической и математической точек зрения, начиная с простых древних шифров замены, переходя к механическим шифрам, современной криптографии с симметричным и открытым ключом, и заканчивая эрой &amp;ldquo;постквантовой криптографии (PQC)&amp;rdquo;, которая наступит с практическим применением квантовых компьютеров.&lt;/p>
&lt;hr>
&lt;h1 id="2-эпоха-классической-криптографии-замена-и-перестановка-символов">2. Эпоха классической криптографии: Замена и перестановка символов
&lt;/h1>&lt;p>Происхождение криптографии восходит к временам до нашей эры. Ранние шифры в основном состояли из двух подходов: &amp;ldquo;перестановка&amp;rdquo; и &amp;ldquo;замена&amp;rdquo;.&lt;/p>
&lt;h2 id="шифр-скитала-шифр-перестановки">Шифр Скитала (Шифр перестановки)
&lt;/h2>&lt;p>&amp;ldquo;Скитала&amp;rdquo; (Scytale), использовавшаяся в древней Греции в Спарте в 5 веке до н.э., является одним из старейших криптографических устройств. Длинная полоска пергамента наматывалась на деревянную палочку определенной толщины, и на ней писалось сообщение поперек. Когда пергамент разматывался, буквы оказывались в бессмысленном порядке, но получатель с палочкой такой же толщины мог снова намотать пергамент и прочитать исходное сообщение.&lt;/p>
&lt;h2 id="шифр-цезаря-шифр-простой-замены">Шифр Цезаря (Шифр простой замены)
&lt;/h2>&lt;p>Шифр Цезаря — это шифр, который, как считается, использовал Юлий Цезарь, герой Древнего Рима в 1 веке до н.э. Это шифр моноалфавитной подстановки (Monoalphabetic substitution), который сдвигает алфавит на фиксированное количество позиций (обычно на 3 буквы).&lt;/p>
&lt;p>Математически, если рассматривать буквы как числа от $0$ до $25$, а величину сдвига как $K$, преобразование исходного текста $P$ в зашифрованный текст $C$ выражается следующим уравнением по модулю:&lt;/p>
$$C \equiv P + K \pmod{26}$$
&lt;p>Расшифровка выполняется обратной операцией:&lt;/p>
$$P \equiv C - K \pmod{26}$$
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример простой реализации шифра Цезаря на Python&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">caesar_cipher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">shift&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;encrypt&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;decrypt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shift&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">shift&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">char&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isalpha&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;A&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">char&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isupper&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисление сдвига&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nb">chr&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">base&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">shift&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">26&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">char&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример выполнения&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">plaintext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HELLO WORLD&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">caesar_cipher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plaintext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;encrypt&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Зашифрованный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># KHOOR ZRUOG&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="частотный-анализ-и-шифр-виженера">Частотный анализ и шифр Виженера
&lt;/h2>&lt;p>Шифры моноалфавитной подстановки стали легко взламываться с помощью &amp;ldquo;частотного анализа&amp;rdquo; (Frequency Analysis), изобретенного арабским ученым Аль-Кинди в 9 веке. Он использует статистические особенности языка, например, то, что в английском языке буквы &amp;ldquo;E&amp;rdquo; и &amp;ldquo;T&amp;rdquo; встречаются чаще всего.&lt;/p>
&lt;p>Чтобы противостоять этому, в 16 веке был изобретен &amp;ldquo;шифр Виженера&amp;rdquo; (Vigenère cipher). Это полиалфавитный шифр замены (Polyalphabetic substitution), который циклически переключает несколько сдвигов (ключей), и около 300 лет его называли &amp;ldquo;неразрешимым шифром&amp;rdquo; (Le Chiffre Indéchiffrable).&lt;/p>
&lt;p>Математически $i$-я буква исходного текста $P_i$ и $i$-я буква повторяющегося ключа $K_i$ шифруются следующим образом:&lt;/p>
$$C_i \equiv P_i + K_i \pmod{26}$$
&lt;p>Однако в 19 веке Чарльз Бэббидж и Фридрих Касиски открыли &amp;ldquo;метод Касиски&amp;rdquo; (Kasiski examination), который позволял определить длину ключа по повторяющимся паттернам в зашифрованном тексте, и шифр был взломан.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Классификация классической криптографии"
A["Классическая криптография"] --> B["Шифр перестановки"]
A --> C["Шифр замены"]
B --> D["Шифр Скитала"]
C --> E["Моноалфавитная замена"]
C --> F["Полиалфавитная замена"]
E --> G["Шифр Цезаря"]
F --> H["Шифр Виженера"]
end&lt;/div>
&lt;hr>
&lt;h1 id="3-механическая-криптография-и-мировые-войны-энигма-и-ее-расшифровка">3. Механическая криптография и мировые войны: Энигма и ее расшифровка
&lt;/h1>&lt;p>В начале 20 века средства связи перешли от писем к телеграфу и радио, что потребовало увеличения скорости и сложности шифрования. В это время появилась &amp;ldquo;механическая криптография&amp;rdquo;, использующая комбинацию роторов (вращающихся дисков).&lt;/p>
&lt;h2 id="угроза-энигмы-enigma">Угроза Энигмы (Enigma)
&lt;/h2>&lt;p>&amp;ldquo;Энигма&amp;rdquo;, использовавшаяся нацистской Германией во время Второй мировой войны, является самой известной шифровальной машиной в истории криптографии. Энигма состояла из нескольких роторов (обычно 3-4), коммутационной панели (Steckerbrett), меняющей проводку букв, и рефлектора (отражающего ротора).&lt;/p>
&lt;p>Каждый раз при вводе буквы с клавиатуры ротор вращался, поэтому даже при последовательном вводе одной и той же буквы выводились разные зашифрованные символы (высшая форма полиалфавитного шифра). Пространство ключей (комбинаций настроек) составляло около $1.58 \times 10^{19}$ вариантов (около 15,8 квинтиллионов), и считалось, что взломать ее полным перебором при технологиях того времени было невозможно.&lt;/p>
&lt;h2 id="алан-тьюринг-и-бомба-bombe">Алан Тьюринг и &amp;ldquo;Бомба&amp;rdquo; (Bombe)
&lt;/h2>&lt;p>Вызов этой неприступной Энигме бросила команда криптоаналитиков из Блетчли-парка в Великобритании, опираясь на ранние результаты польского математика Мариана Реевского и его коллег.&lt;/p>
&lt;p>В частности, Алан Тьюринг (Alan Turing) использовал предположения об исходном тексте, соответствующем части зашифрованного текста (криб: Crib), и разработал электромеханическую машину для расшифровки, названную &amp;ldquo;Бомба&amp;rdquo; (Bombe). &amp;ldquo;Бомба&amp;rdquo; быстро обнаруживала логические противоречия и последовательно исключала невозможные настройки роторов, что позволило успешно расшифровать Энигму. Считается, что это великое достижение приблизило победу союзников на несколько лет.&lt;/p>
&lt;hr>
&lt;h1 id="4-начало-современной-криптографии-криптография-с-симметричным-ключом-des-и-aes">4. Начало современной криптографии: Криптография с симметричным ключом (DES и AES)
&lt;/h1>&lt;p>После войны с появлением компьютеров шифрование претерпело радикальную смену парадигмы: от манипуляций с &amp;ldquo;символами&amp;rdquo; к операциям с &amp;ldquo;битами (0 и 1)&amp;rdquo;.&lt;/p>
&lt;h2 id="клод-шеннон-и-теория-информации">Клод Шеннон и теория информации
&lt;/h2>&lt;p>В 1949 году Клод Шеннон опубликовал работу &amp;ldquo;Теория связи в секретных системах&amp;rdquo;, заложив математические основы современной криптографии. Он предложил &amp;ldquo;запутывание&amp;rdquo; (Confusion) и &amp;ldquo;рассеивание&amp;rdquo; (Diffusion) в качестве принципов безопасного проектирования шифров.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Запутывание (Confusion)&lt;/strong>: Сделать взаимосвязь между ключом и зашифрованным текстом как можно более сложной. (Реализуется через замену / S-блоки)&lt;/li>
&lt;li>&lt;strong>Рассеивание (Diffusion)&lt;/strong>: Сделать так, чтобы изменение одного бита исходного текста влияло на множество битов зашифрованного текста. (Реализуется через перестановку / перемешивание)&lt;/li>
&lt;/ul>
&lt;h2 id="des-data-encryption-standard">DES (Data Encryption Standard)
&lt;/h2>&lt;p>В 1977 году Национальный институт стандартов и технологий США (NIST, тогда NBS) принял &amp;ldquo;DES&amp;rdquo;, основанный на дизайне IBM, в качестве стандартного шифра.
DES использует архитектуру, называемую &amp;ldquo;Сеть Фейстеля&amp;rdquo; (Feistel Network), имеет размер блока 64 бита и длину ключа 56 бит. Это имело преимущество в реализации, так как алгоритмы шифрования и дешифрования имели почти одинаковую структуру.&lt;/p>
&lt;p>Однако по мере роста вычислительных мощностей компьютеров стало ясно, что длины ключа в 56 бит (около $7.2 \times 10^{16}$ вариантов) недостаточно. В 1998 году Electronic Frontier Foundation (EFF) разработал специальную машину &amp;ldquo;Deep Crack&amp;rdquo;, которая смогла взломать DES за несколько дней.&lt;/p>
&lt;h2 id="aes-advanced-encryption-standard">AES (Advanced Encryption Standard)
&lt;/h2>&lt;p>В качестве нового стандарта, пришедшего на смену DES, в 2001 году был принят &amp;ldquo;AES&amp;rdquo;. Был выбран алгоритм &amp;ldquo;Rijndael&amp;rdquo;, разработанный бельгийскими криптографами по итогам открытого конкурса.&lt;/p>
&lt;p>AES использует не Сеть Фейстеля, а &amp;ldquo;SPN-структуру&amp;rdquo; (Substitution-Permutation Network) и применяет математические операции в поле Галуа (конечном поле) $GF(2^8)$. Длину ключа можно выбрать из 128, 192 и 256 бит, и сегодня он широко используется по всему миру как стандартный симметричный алгоритм шифрования.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "1 раунд обработки AES (SPN-структура)"
A["Состояние входа (128-bit)"] --> B("SubBytes (Байтовая замена / S-Box)")
B --> C("ShiftRows (Сдвиг строк)")
C --> D("MixColumns (Смешивание столбцов / Умножение в GF(2^8))")
D --> E("AddRoundKey (XOR с раундовым ключом)")
E --> F["Переход к следующему раунду"]
end&lt;/div>
&lt;hr>
&lt;h1 id="5-революция-криптографии-с-открытым-ключом-от-диффи-хеллмана-к-rsa">5. Революция криптографии с открытым ключом: От Диффи-Хеллмана к RSA
&lt;/h1>&lt;p>Криптография с симметричным ключом имела существенный недостаток. Это &amp;ldquo;проблема распределения ключей&amp;rdquo; (Key Distribution Problem). Проблема заключалась в том, как безопасно разделить &amp;ldquo;общий ключ&amp;rdquo; с удаленным абонентом до начала зашифрованного общения. Эта проблема была решена в 1970-х годах с появлением &amp;ldquo;криптографии с открытым ключом&amp;rdquo;.&lt;/p>
&lt;h2 id="обмен-ключами-диффи-хеллмана">Обмен ключами Диффи-Хеллмана
&lt;/h2>&lt;p>В 1976 году Уитфилд Диффи и Мартин Хеллман опубликовали прорывную статью &amp;ldquo;Новые направления в криптографии&amp;rdquo;. Они предложили метод безопасного совместного использования ключа даже по прослушиваемому каналу связи, используя математическую сложность &amp;ldquo;задачи дискретного логарифмирования&amp;rdquo; (Discrete Logarithm Problem).&lt;/p>
&lt;ol>
&lt;li>Публикуются большое простое число $p$ и генератор $g$.&lt;/li>
&lt;li>Алиса выбирает секретное значение $a$ и отправляет Бобу $A = g^a \pmod{p}$.&lt;/li>
&lt;li>Боб выбирает секретное значение $b$ и отправляет Алисе $B = g^b \pmod{p}$.&lt;/li>
&lt;li>Алиса вычисляет $K = B^a \pmod{p}$, а Боб вычисляет $K = A^b \pmod{p}$.&lt;/li>
&lt;li>По закону степеней $K = (g^b)^a = (g^a)^b = g^{ab} \pmod{p}$, и они успешно разделяют один и тот же ключ $K$.&lt;/li>
&lt;/ol>
&lt;h2 id="алгоритм-rsa">Алгоритм RSA
&lt;/h2>&lt;p>В следующем 1977 году Рон Ривест, Ади Шамир и Леонард Адлеман изобрели &amp;ldquo;алгоритм RSA&amp;rdquo;. Он основан на том свойстве, что &amp;ldquo;разложение огромного составного числа на простые множители является сложным&amp;rdquo;.&lt;/p>
&lt;p>&lt;strong>Математический механизм RSA:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Выбираются два огромных простых числа $p$ и $q$, и вычисляется $n = p \times q$.&lt;/li>
&lt;li>Вычисляется функция Эйлера $\phi(n) = (p-1)(q-1)$.&lt;/li>
&lt;li>Выбирается целое число $e$ (открытый ключ), взаимно простое с $\phi(n)$.&lt;/li>
&lt;li>Вычисляется целое число $d$ (закрытый ключ), такое что $e \times d \equiv 1 \pmod{\phi(n)}$.&lt;/li>
&lt;/ol>
&lt;p>Шифрование: Для исходного текста $M$ вычисляется $C \equiv M^e \pmod{n}$
Дешифрование: Для зашифрованного текста $C$ вычисляется $M \equiv C^d \pmod{n}$&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Концептуальный код RSA на Python (не для практического использования)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">ext_euclid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Вычисление модульного обратного с помощью расширенного алгоритма Евклида&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ext_euclid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">g&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">rsa_example&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Пример с небольшими простыми числами&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">61&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">53&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">17&lt;/span> &lt;span class="c1"># Значение, взаимно простое с phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ext_euclid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Открытый ключ: (e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, n=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Закрытый ключ: (d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, n=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Шифрование и расшифровка сообщения&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">65&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decrypted&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Исходный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> -&amp;gt; Зашифрованный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> -&amp;gt; После расшифровки: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rsa_example&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h1 id="6-расцвет-криптографии-на-эллиптических-кривых-ecc">6. Расцвет криптографии на эллиптических кривых (ECC)
&lt;/h1>&lt;p>RSA — мощный алгоритм, но по мере роста производительности компьютеров возникла проблема увеличения вычислительных затрат из-за необходимости увеличивать длину ключа для поддержания безопасности (в настоящее время 2048 или 3072 бита).&lt;/p>
&lt;p>Поэтому в 1985 году была предложена &amp;ldquo;криптография на эллиптических кривых&amp;rdquo; (Elliptic Curve Cryptography: ECC). Она использует сложение точек на эллиптической кривой (обычно вида $y^2 = x^3 + ax + b$) над конечным полем.&lt;/p>
&lt;p>Известно, что задачу дискретного логарифмирования на эллиптической кривой (ECDLP) решить еще сложнее, чем задачу факторизации, и &lt;strong>ECC может обеспечить уровень безопасности, эквивалентный 3072 битам RSA, при длине ключа всего 256 бит&lt;/strong>. Это позволило использовать быструю и безопасную зашифрованную связь (например, ECDSA и ECDH) даже в средах с ограниченными вычислительными ресурсами, таких как смартфоны и устройства IoT.&lt;/p>
&lt;hr>
&lt;h1 id="7-угроза-квантовых-компьютеров-и-постквантовая-криптография-pqc">7. Угроза квантовых компьютеров и постквантовая криптография (PQC)
&lt;/h1>&lt;p>Казалось, что криптографические технологии непоколебимы, но алгоритм Шора (Shor&amp;rsquo;s algorithm), опубликованный Питером Шором в 1994 году, вызвал огромный шок.&lt;/p>
&lt;p>Квантовые компьютеры выполняют вычисления, используя такие свойства квантовой механики, как &amp;ldquo;суперпозиция&amp;rdquo; и &amp;ldquo;квантовая запутанность&amp;rdquo;. Было математически доказано, что если выполнить алгоритм Шора на квантовом компьютере достаточной мощности, то задача факторизации и задача дискретного логарифмирования могут быть решены за &amp;ldquo;полиномиальное время&amp;rdquo;. Это означает, что в день появления практичного квантового компьютера (Q-Day) вся используемая сегодня криптография с открытым ключом, такая как RSA и ECC, мгновенно будет сломана.&lt;/p>
&lt;h2 id="появление-pqc-post-quantum-cryptography">Появление PQC (Post-Quantum Cryptography)
&lt;/h2>&lt;p>В ответ на эту беспрецедентную угрозу быстрыми темпами ведутся исследования в области &amp;ldquo;постквантовой криптографии&amp;rdquo; (PQC), основанной на новых математических задачах, которые трудно решить даже на квантовых компьютерах. NIST (Национальный институт стандартов и технологий США) в течение многих лет ведет процесс стандартизации PQC, и в основном рассматриваются следующие математические подходы.&lt;/p>
&lt;h3 id="1-криптография-на-решетках-lattice-based-cryptography">1. Криптография на решетках (Lattice-based Cryptography)
&lt;/h3>&lt;p>В настоящее время это наиболее перспективный подход, который был принят в стандартах алгоритмов NIST (ML-KEM / Kyber, ML-DSA / Dilithium). Он основан на сложности задачи поиска определенной точки в многомерной &amp;ldquo;решетке&amp;rdquo; (Lattice) (например, задача поиска кратчайшего вектора: SVP) и задачи обучения с ошибками (Learning With Errors: LWE).&lt;/p>
&lt;p>Концепция задачи LWE использует то свойство, что если намеренно добавить &amp;ldquo;небольшой шум (ошибку)&amp;rdquo; в систему линейных уравнений, найти решение становится чрезвычайно сложно.
Система уравнений: $\mathbf{A}\mathbf{s} + \mathbf{e} \equiv \mathbf{b} \pmod{q}$
($\mathbf{A}$ и $\mathbf{b}$ публичны, $\mathbf{s}$ — секретный ключ, $\mathbf{e}$ — небольшой шум)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Концептуальный псевдокод задачи LWE (для обучения)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">256&lt;/span> &lt;span class="c1"># Размерность&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3329&lt;/span> &lt;span class="c1"># Модуль&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">512&lt;/span> &lt;span class="c1"># Количество уравнений&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Секретный ключ s и небольшая ошибка e&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Публичная матрица A и публичный вектор b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">A&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">A&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Считается, что восстановить s из A и b очень сложно даже с использованием квантовых компьютеров&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-криптография-на-основе-хешей-hash-based-cryptography">2. Криптография на основе хешей (Hash-based Cryptography)
&lt;/h3>&lt;p>Это схема цифровой подписи, безопасность которой основывается исключительно на стойкости хеш-функций к коллизиям. Поскольку она не имеет математической структуры, она устойчива к квантовым атакам, но размер подписи, как правило, получается большим (например, SPHINCS+).&lt;/p>
&lt;h3 id="3-криптография-на-основе-кодов-code-based-cryptography">3. Криптография на основе кодов (Code-based Cryptography)
&lt;/h3>&lt;p>Метод шифрования, основанный на теории кодов, исправляющих ошибки. Известным примером является криптосистема Мак-Элиса, предложенная в 1978 году. Она имеет долгую историю и признанную надежность, но ее недостатком является очень большой размер открытого ключа (который может достигать нескольких мегабайт).&lt;/p>
&lt;div class="mermaid">timeline
title "История эволюции криптографии и вычислительных машин"
"Древность - Средние века" : "Шифр Цезаря" : "Шифр Виженера" : "Рождение частотного анализа"
"1930-40-е годы" : "Использование и взлом Энигмы" : "Разработка машин Тьюринга и Бомбы"
"1970-е годы" : "Стандартизация DES (1977)" : "Обмен ключами Диффи-Хеллмана (1976)" : "Создание алгоритма RSA (1977)"
"1980-90-е годы" : "Предложение криптографии на эллиптических кривых (ECC)" : "Публикация алгоритма Шора (1994)"
"2000-е годы" : "Стандартизация AES (2001)"
"С 2010-х по настоящее время" : "Ускорение исследований квантовых компьютеров" : "Начало проекта стандартизации PQC от NIST"
"Ближайшее будущее (Q-Day)" : "Создание крупномасштабного квантового компьютера?" : "Полный переход на PQC (ML-KEM/ML-DSA)"&lt;/div>
&lt;hr>
&lt;h1 id="8-заключение-бесконечная-битва-щита-и-меча">8. Заключение: Бесконечная битва щита и меча
&lt;/h1>&lt;p>История криптографии — это история бесконечной борьбы между изобретением новых методов шифрования (щит) и новых методов взлома (меч).&lt;/p>
&lt;p>Шифр Цезаря пал от частотного анализа, а непобедимая Энигма пала перед гениальным умом Тьюринга и силой машин. И теперь даже мощные криптосистемы, такие как RSA и ECC, которые поддерживают основу современного интернет-общества, находятся под угрозой нового &amp;ldquo;меча&amp;rdquo; — квантового компьютера.&lt;/p>
&lt;p>Однако человечество уже смотрит в будущее и готовит новый &amp;ldquo;щит&amp;rdquo; — постквантовую криптографию (PQC). Сегодня обеспечение перехода от существующей криптографии с открытым ключом к PQC (обеспечение криптографической гибкости - Crypto Agility) является насущной задачей для IT-инфраструктуры во всем мире.&lt;/p>
&lt;p>Криптография — это не просто сложная математическая головоломка, это сильнейший барьер для защиты нашей конфиденциальности, нашего имущества и самой социальной инфраструктуры.&lt;/p></description></item><item><title>【Математическое объяснение】Как работает алгоритм RSA, объяснено для старшеклассников</title><link>http://kenji.blog/ru/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/rsa-encryption-math-explained-for-beginners/</guid><description>&lt;img src="http://kenji.blog/p/rsa-encryption-math-explained-for-beginners/img/eyecatch.jpg" alt="Featured image of post 【Математическое объяснение】Как работает алгоритм RSA, объяснено для старшеклассников" />&lt;p>Одной из технологий, фундаментально обеспечивающих безопасность интернет-общества, является алгоритм RSA (шифрование RSA). Оплата кредитными картами в интернет-магазинах, общение с друзьями в соцсетях, отправка и получение конфиденциальной корпоративной информации — большая часть связи, которую мы небрежно используем каждый день, защищена алгоритмом RSA или технологиями-преемниками.&lt;/p>
&lt;p>Однако при слове &amp;ldquo;криптография&amp;rdquo; (шифрование) вы можете представить себе сложные шифровальные машины из шпионских фильмов или сверхсложную математику, понятную лишь избранным гениям. Безусловно, современная теория криптографии основана на продвинутой математике, но &lt;strong>фундаментальный механизм алгоритма RSA вполне можно понять, обладая знаниями математики на уровне старшей школы (свойства целых чисел, простые числа, сравнения по модулю и т.д.)&lt;/strong>.&lt;/p>
&lt;p>В этой статье, взяв за отправную точку знания школьной математики, мы шаг за шагом подробно объясним, на каких математических принципах работает алгоритм RSA и почему его так сложно взломать. Мы будем использовать конкретные примеры и объяснять все так тщательно, чтобы даже те, кто немного не в ладах с математикой, смогли во всем разобраться.&lt;/p>
&lt;hr>
&lt;h2 id="1-симметричное-и-асимметричное-шифрование-с-открытым-ключом">1. Симметричное и асимметричное шифрование (с открытым ключом)
&lt;/h2>&lt;p>Прежде чем переходить к математическим механизмам алгоритма RSA, давайте проясним базовые концепции криптографии. Методы шифрования в целом делятся на два типа: &amp;ldquo;шифрование с симметричным ключом&amp;rdquo; и &amp;ldquo;шифрование с открытым ключом&amp;rdquo; (асимметричное).&lt;/p>
&lt;h3 id="11-ограничения-симметричного-шифрования">1.1 Ограничения симметричного шифрования
&lt;/h3>&lt;p>Большинство шифров, используемых с древних времен, называются &amp;ldquo;системами симметричного шифрования&amp;rdquo;. Это метод, при котором &lt;strong>для &amp;ldquo;шифрования&amp;rdquo; (превращения сообщения в секретный шифр) и &amp;ldquo;расшифрования&amp;rdquo; (возвращения зашифрованного текста в исходное сообщение) используется один и тот же ключ&lt;/strong>.&lt;/p>
&lt;p>Например, предположим, что Алиса отправляет Бобу секретное письмо. Алиса кладет письмо в шкатулку и запирает ее на навесной замок (общий ключ). Чтобы Боб мог открыть эту шкатулку, у него должен быть точно такой же ключ, какой использовала Алиса.&lt;/p>
&lt;p>У этого метода есть серьезная проблема: &amp;ldquo;проблема распределения ключей&amp;rdquo;. Если находящиеся далеко друг от друга Алиса и Боб связываются впервые, как им безопасно обменяться ключами, чтобы их никто не перехватил? Если ключ украдет третье лицо во время пересылки, все последующие зашифрованные сообщения станут достоянием гласности.&lt;/p>
&lt;h3 id="12-революционное-изобретение-шифрование-с-открытым-ключом">1.2 Революционное изобретение: &amp;ldquo;Шифрование с открытым ключом&amp;rdquo;
&lt;/h3>&lt;p>Для решения этой проблемы с распределением ключей была изобретена &amp;ldquo;система шифрования с открытым ключом&amp;rdquo;. Алгоритм RSA относится к этому типу.&lt;/p>
&lt;p>В системе с открытым ключом используются &lt;strong>два разных ключа: &amp;ldquo;ключ для шифрования (открытый ключ)&amp;rdquo; и &amp;ldquo;ключ для расшифрования (закрытый ключ)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>Получатель, Боб, создает пару ключей: &amp;ldquo;открытый ключ&amp;rdquo; и &amp;ldquo;закрытый ключ&amp;rdquo;.&lt;/li>
&lt;li>Боб публикует свой &amp;ldquo;открытый ключ&amp;rdquo; на весь мир (неважно, кто его получит).&lt;/li>
&lt;li>Отправительница, Алиса, использует &amp;ldquo;открытый ключ&amp;rdquo; Боба, чтобы зашифровать сообщение и отправить его.&lt;/li>
&lt;li>Зашифрованное сообщение можно расшифровать только с помощью &amp;ldquo;закрытого ключа&amp;rdquo;, который есть только у Боба.&lt;/li>
&lt;/ol>
&lt;p>Если снова привести аналогию с навесным замком, то Боб создает множество &amp;ldquo;открытых навесных замков (открытых ключей)&amp;rdquo; и разбрасывает их по всему миру. Алиса кладет свое сообщение для Боба в шкатулку, берет один из замков Боба и защелкивает его. Как только замок закрыт, его можно открыть только с помощью &amp;ldquo;мастер-ключа (закрытого ключа)&amp;rdquo;, который есть у Боба. Даже если кто-то украдет шкатулку по дороге, он не сможет ее открыть без мастер-ключа.&lt;/p>
&lt;div class="mermaid">graph TD
A["Алиса (отправитель)"] --> B["Открытый текст (сообщение)"]
B --> C["Процесс шифрования"]
D["Открытый ключ Боба (доступен всем)"] --> C
C --> E["Отправка через интернет: Зашифрованный текст"]
E --> F["Процесс расшифрования"]
G["Закрытый ключ Боба (только у Боба)"] --> F
F --> H["Восстановленный открытый текст (сообщение)"]
H --> I["Боб (получатель)"]&lt;/div>
&lt;p>Чтобы реализовать эту революционную систему, потребовалась своего рода &lt;strong>&amp;ldquo;односторонняя функция (математическая головоломка с односторонним движением)&amp;rdquo;&lt;/strong>, которая позволяет &amp;ldquo;легко зашифровать с помощью открытого ключа, но абсолютно невозможно расшифровать без закрытого&amp;rdquo;. И в качестве деталей для этой головоломки были выбраны хорошо знакомые нам &amp;ldquo;простые числа&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="2-математические-основы-rsa-1-простые-числа-и-факторизация">2. Математические основы RSA 1: Простые числа и факторизация
&lt;/h2>&lt;p>Безопасность алгоритма RSA основана на математическом факте: &lt;strong>&amp;ldquo;факторизация (разложение на простые множители) огромных чисел — чрезвычайно сложная задача&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h3 id="21-что-такое-простые-числа">2.1 Что такое простые числа?
&lt;/h3>&lt;p>Простое число — это &amp;ldquo;натуральное число больше 1, которое делится без остатка только на 1 и на само себя&amp;rdquo;.
Пример: $2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>Простые числа — это словно &amp;ldquo;атомы&amp;rdquo; всех целых чисел. Любое натуральное число можно разложить в виде произведения простых чисел. Это называется &lt;strong>факторизацией (разложением на простые множители)&lt;/strong>. Например, $60 = 2^2 \times 3 \times 5$. Тот факт, что любое число можно разложить на простые множители единственным образом (если не учитывать порядок), известен как &amp;ldquo;основная теорема арифметики&amp;rdquo;.&lt;/p>
&lt;h3 id="22-сложность-факторизации-односторонняя-функция">2.2 Сложность факторизации (Односторонняя функция)
&lt;/h3>&lt;p>Здесь важна асимметрия: &lt;strong>&amp;ldquo;умножать легко, а раскладывать на множители — сложно&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Например, попробуйте вычислить в уме произведение следующих двух простых чисел:
$11 \times 13 = ?$
Это просто. Ответ $143$.&lt;/p>
&lt;p>А как насчет следующего числа?
Разложите на простые множители $323$.
Ну как? Это должно занять некоторое время. (Ответ $17 \times 19$).&lt;/p>
&lt;p>Если числа небольшие, человек еще может как-то справиться, но по мере увеличения чисел вычисления становятся взрывообразно сложными даже для компьютеров. В распространенном сегодня алгоритме RSA используется число $N = p \times q$, полученное путем перемножения двух невероятно огромных простых чисел $p$ и $q$ размером 2048 бит (около 600 цифр в десятичной системе).&lt;/p>
&lt;p>Когда даны два огромных простых числа $p$ и $q$, вычисление $N$ занимает у компьютера долю секунды (меньше миллисекунды). Однако, если дано только $N$, поиск исходных $p$ и $q$ займет столько времени, что даже самые быстрые суперкомпьютеры современности не смогут решить эту задачу и за триллионы лет.&lt;/p>
&lt;p>Эта &lt;strong>&amp;ldquo;вычислительная асимметрия (в одну сторону легко, в обратную — сложно)&amp;rdquo;&lt;/strong> является фундаментом для создания связи между открытым и закрытым ключами.&lt;/p>
&lt;hr>
&lt;h2 id="3-математические-основы-rsa-2-сравнения-арифметика-остатков--модульная-арифметика">3. Математические основы RSA 2: Сравнения (Арифметика остатков / модульная арифметика)
&lt;/h2>&lt;p>Вычисления в алгоритме RSA выполняются не в виде привычных нам сложений и умножений, где числа могут бесконечно расти, а в мире &amp;ldquo;остатков&amp;rdquo; от деления на определенное число. Это называется &lt;strong>сравнением по модулю (модульной арифметикой)&lt;/strong>.&lt;/p>
&lt;h3 id="31-математика-часов">3.1 Математика часов
&lt;/h3>&lt;p>Модульную арифметику часто сравнивают с &amp;ldquo;математикой часов&amp;rdquo;. Предположим, сейчас 10 часов. Который час будет через 5 часов? $10 + 5 = 15$ часов, но на обычных 12-часовых часах мы ответим &amp;ldquo;3 часа&amp;rdquo;. Это потому, что остаток от деления 15 на 12 равен 3.&lt;/p>
&lt;p>В математике это записывается следующим образом:
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
Это читается как &amp;ldquo;15 сравнимо с 3 по модулю 12 (остатки от деления на 12 равны)&amp;rdquo;.&lt;/p>
&lt;h3 id="32-основные-свойства-сравнений">3.2 Основные свойства сравнений
&lt;/h3>&lt;p>Сравнения обладают очень удобными свойствами, похожими на свойства обычных уравнений ($=$). Пусть модуль (делитель) равен $N$.
Если $a \equiv b \pmod N$ и $c \equiv d \pmod N$, то выполняется следующее:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Сложение:&lt;/strong> $a + c \equiv b + d \pmod N$&lt;/li>
&lt;li>&lt;strong>Вычитание:&lt;/strong> $a - c \equiv b - d \pmod N$&lt;/li>
&lt;li>&lt;strong>Умножение:&lt;/strong> $a \times c \equiv b \times d \pmod N$&lt;/li>
&lt;li>&lt;strong>Возведение в степень:&lt;/strong> $a^k \equiv b^k \pmod N$ (где $k$ — натуральное число)&lt;/li>
&lt;/ol>
&lt;p>Особенно важным является свойство &amp;ldquo;возведения в степень&amp;rdquo;. Оно означает, что &lt;strong>&amp;ldquo;остаток от степени равен степени от остатка&amp;rdquo;&lt;/strong>.
Например, мы хотим найти остаток от деления $7^{100}$ на $5$. Честно умножать $7$ само на себя 100 раз, а затем делить на $5$ было бы очень сложно. Но используя свойства сравнений, поскольку $7 \equiv 2 \pmod 5$, мы получаем $7^{100} \equiv 2^{100} \pmod 5$, что позволяет кардинально упростить вычисления. В мире криптографии мы работаем с возведением в степень очень больших чисел, поэтому это свойство просто необходимо.&lt;/p>
&lt;hr>
&lt;h2 id="4-математические-основы-rsa-3-функция-эйлера-и-теорема-эйлера">4. Математические основы RSA 3: Функция Эйлера и теорема Эйлера
&lt;/h2>&lt;p>Здесь начинается магическая математика, составляющая ядро RSA. Появляется &amp;ldquo;теорема Эйлера&amp;rdquo;, которая является обобщением &amp;ldquo;малой теоремы Ферма&amp;rdquo;.&lt;/p>
&lt;h3 id="41-функция-эйлера-тотиент-phin">4.1 Функция Эйлера (тотиент) $\phi(N)$
&lt;/h3>&lt;p>Функция Эйлера (функция $\phi$) для некоторого натурального числа $N$ — это функция, которая возвращает &lt;strong>&amp;ldquo;количество натуральных чисел от 1 до $N$, которые взаимно просты с $N$ (наибольший общий делитель равен 1)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Давайте рассмотрим несколько примеров.&lt;/p>
&lt;ul>
&lt;li>$\phi(5)$: Среди 1, 2, 3, 4, 5 взаимно простыми с 5 являются 1, 2, 3, 4 — всего 4 числа. Следовательно, $\phi(5) = 4$.&lt;/li>
&lt;li>$\phi(6)$: Среди 1, 2, 3, 4, 5, 6 взаимно простыми с 6 являются 1, 5 — всего 2 числа. Следовательно, $\phi(6) = 2$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【Особое свойство для простых чисел】&lt;/strong>
Если $p$ — простое число, все числа от 1 до $p-1$ взаимно просты с $p$. Следовательно,
&lt;/p>
$$ \phi(p) = p - 1 $$
&lt;p>&lt;strong>【Особое свойство для произведения простых чисел】&lt;/strong>
Для двух различных простых чисел $p$ и $q$, если $N = p \times q$, то $\phi(N)$ можно легко вычислить следующим образом:
&lt;/p>
$$ \phi(N) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$
&lt;p>
Это свойство служит &amp;ldquo;потайной дверью&amp;rdquo; (лазейкой) в алгоритме RSA. Тот, кто знает $p$ и $q$ (создатель ключа), может вычислить $\phi(N)$ за мгновение, но третье лицо, знающее только $N$, не сможет вычислить $\phi(N)$ без разложения $N$ на простые множители.&lt;/p>
&lt;h3 id="42-теорема-эйлера">4.2 Теорема Эйлера
&lt;/h3>&lt;p>Леонард Эйлер, используя функцию $\phi(N)$, доказал следующую красивую теорему.&lt;/p>
&lt;p>&lt;strong>Теорема Эйлера:&lt;/strong>
Если целое число $a$ и $N$ взаимно просты, то выполняется следующее сравнение:
&lt;/p>
$$ a^{\phi(N)} \equiv 1 \pmod N $$
&lt;p>Это удивительное свойство: &amp;ldquo;если умножить некоторое число $a$ само на себя $\phi(N)$ раз и разделить на $N$, остаток всегда будет равен $1$&amp;rdquo;. (Если $N$ — простое число $p$, это принимает вид $a^{p-1} \equiv 1 \pmod p$ и называется малой теоремой Ферма).&lt;/p>
&lt;p>Давайте преобразуем эту теорему Эйлера. Умножим обе части на $a$ еще раз.
&lt;/p>
$$ a^{\phi(N) + 1} \equiv a \pmod N $$
&lt;p>Более того, для любого целого числа $k$, $a^{k \cdot \phi(N)}$ также будет равно $1^k = 1$, поэтому выполняется следующее равенство:
&lt;/p>
$$ a^{k \cdot \phi(N) + 1} \equiv a \pmod N $$
&lt;p>Именно эта формула является фундаментальным принципом, на котором строится магия RSA: &lt;strong>&amp;ldquo;после шифрования и расшифрования мы возвращаемся к исходному значению&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="5-алгоритм-rsa-шаги-генерации-ключей-шифрования-и-расшифрования">5. Алгоритм RSA: Шаги генерации ключей, шифрования и расшифрования
&lt;/h2>&lt;p>Теперь, когда у нас есть базовые знания, давайте посмотрим на конкретные шаги алгоритма RSA. Алгоритм RSA можно разделить на три основные фазы: &amp;ldquo;1. Генерация ключей&amp;rdquo;, &amp;ldquo;2. Шифрование&amp;rdquo; и &amp;ldquo;3. Расшифрование&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Выбрать простые числа p, q"] --> A2["Вычислить N = p × q"]
A1 --> A3["Вычислить φ(N) = (p-1)(q-1)"]
A3 --> A4["Выбрать e, взаимно простое с φ(N)"]
A3 --> A5["Вычислить d такое, что e × d ≡ 1 (mod φ(N))"]
A2 --> A6["Открытый ключ (N, e)"]
A4 --> A6
A5 --> A7["Закрытый ключ d"]
B1["2. Открытый текст (сообщение) M"] --> B2["Вычислить C ≡ M^e (mod N)"]
A6 -.-> B2
B2 --> B3["Отправить зашифрованный текст C"]
B3 --> C1["3. Полученный зашифрованный текст C"]
C1 --> C2["Вычислить M ≡ C^d (mod N)"]
A7 -.-> C2
C2 --> C3["Получить исходный открытый текст M"]&lt;/div>
&lt;h3 id="51-генерация-ключей-key-generation">5.1 Генерация ключей (Key Generation)
&lt;/h3>&lt;p>Получатель, Боб, генерирует для себя &amp;ldquo;открытый ключ&amp;rdquo; и &amp;ldquo;закрытый ключ&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Выбор простых чисел:&lt;/strong> Случайным образом выбираются два больших простых числа $p$ и $q$.&lt;/li>
&lt;li>&lt;strong>Вычисление модуля $N$:&lt;/strong> Вычисляется $N = p \times q$. Это $N$ становится публичным.&lt;/li>
&lt;li>&lt;strong>Вычисление $\phi(N)$:&lt;/strong> Вычисляется функция Эйлера $\phi(N) = (p - 1)(q - 1)$. Это секретное число Боба.&lt;/li>
&lt;li>&lt;strong>Выбор открытого ключа $e$:&lt;/strong> Выбирается целое число $e$ такое, что $1 &lt; e &lt; \phi(N)$ и $e$ взаимно просто с $\phi(N)$.&lt;/li>
&lt;li>&lt;strong>Вычисление закрытого ключа $d$:&lt;/strong> Находится целое число $d$, удовлетворяющее следующему условию:
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
Иными словами, &amp;ldquo;$d$ — это такое число, при котором остаток от деления $e \times d$ на $\phi(N)$ равен $1$&amp;rdquo;.&lt;/li>
&lt;/ol>
&lt;p>На этом подготовка ключей завершена.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Открытый ключ:&lt;/strong> Пара $(N, e)$. Публикуется для всего мира.&lt;/li>
&lt;li>&lt;strong>Закрытый ключ:&lt;/strong> $d$. Никогда никому не передается.&lt;/li>
&lt;/ul>
&lt;h3 id="52-шифрование-encryption">5.2 Шифрование (Encryption)
&lt;/h3>&lt;p>Допустим, Алиса хочет отправить Бобу секретное сообщение $M$. ($M$ — это числовое представление текста, причем $0 \le M &lt; N$). Алиса использует открытый ключ Боба $(N, e)$ и выполняет следующие вычисления:&lt;/p>
$$ C \equiv M^e \pmod N $$
&lt;p>Она вычисляет &amp;ldquo;остаток $C$ от деления сообщения $M$ в степени $e$ на $N$&amp;rdquo;. Это $C$ и есть зашифрованный текст.&lt;/p>
&lt;h3 id="53-расшифрование-decryption">5.3 Расшифрование (Decryption)
&lt;/h3>&lt;p>Боб получает зашифрованный текст $C$. Боб использует закрытый ключ $d$ и выполняет следующие вычисления:&lt;/p>
$$ M \equiv C^d \pmod N $$
&lt;p>Если вычислить &amp;ldquo;остаток от деления зашифрованного текста $C$ в степени $d$ на $N$&amp;rdquo;, то удивительным образом восстанавливается исходное сообщение $M$!&lt;/p>
&lt;hr>
&lt;h2 id="6-почему-расшифрование-возвращает-исходное-сообщение-математическое-доказательство">6. Почему расшифрование возвращает исходное сообщение? (Математическое доказательство)
&lt;/h2>&lt;p>Вы можете задаться вопросом: &amp;ldquo;Как так получается, что простое возведение $C$ в степень $d$ возвращает нас к исходному $M$?&amp;rdquo;. Вот тут-то и проявляет свою силу ранее упомянутая &amp;ldquo;теорема Эйлера&amp;rdquo;.&lt;/p>
&lt;p>Давайте подставим формулу шифрования $C = M^e$ в формулу расшифрования $C^d \pmod N$.
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>Здесь вспомните шаг 5 генерации ключей. Боб при создании $d$ выбрал его так, чтобы выполнялось условие $e \times d \equiv 1 \pmod{\phi(N)}$. Это означает, что &amp;ldquo;$ed$ — это число, кратное $\phi(N)$, плюс $1$&amp;rdquo;. Используя целое число $k$, это можно записать следующим образом:
&lt;/p>
$$ ed = k \cdot \phi(N) + 1 $$
&lt;p>Подставим это в показатель степени и разложим с помощью правил возведения в степень:
&lt;/p>
$$ M^{ed} = M^{k \cdot \phi(N) + 1} = M^{k \cdot \phi(N)} \times M^1 = (M^{\phi(N)})^k \times M $$
&lt;p>Если предположить, что сообщение $M$ и $N$ взаимно просты, то согласно &lt;strong>теореме Эйлера&lt;/strong> получаем $M^{\phi(N)} \equiv 1 \pmod N$.
&lt;/p>
$$ (M^{\phi(N)})^k \times M \equiv 1^k \times M \equiv M \pmod N $$
&lt;p>Следовательно, блестяще выполняется следующая формула:
&lt;/p>
$$ C^d \equiv M \pmod N $$
&lt;p>Алиса не знает $d$, перехватчик тоже не знает $d$, поэтому получить $M$ из $C$ может только Боб, владеющий ключом $d$.&lt;/p>
&lt;hr>
&lt;h2 id="7-конкретный-пример-давайте-испытаем-rsa-вручную-используя-небольшие-простые-числа">7. Конкретный пример: давайте испытаем RSA вручную, используя небольшие простые числа
&lt;/h2>&lt;p>Давайте попробуем выполнить зашифрованную связь от Алисы к Бобу на практике, используя небольшие числа (простые числа).&lt;/p>
&lt;p>&lt;strong>【Фаза генерации ключей Боба】&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Выбираем два простых числа $p=11$, $q=13$.&lt;/li>
&lt;li>Вычисляем $N = 11 \times 13 = 143$.&lt;/li>
&lt;li>Вычисляем $\phi(N) = (11 - 1) \times (13 - 1) = 10 \times 12 = 120$.&lt;/li>
&lt;li>Выбираем открытый ключ $e$, взаимно простой с $\phi(N)=120$. Пусть $e=7$.&lt;/li>
&lt;li>Находим закрытый ключ $d$. Ищем такое $d$, чтобы выполнялось $7 \times d \equiv 1 \pmod{120}$.
В уравнении $7d = 120k + 1$, если $k=6$, получаем $721$, а $721 \div 7 = 103$.
Следовательно, $d = 103$.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Открытый ключ: $(N=143, e=7)$&lt;/li>
&lt;li>Закрытый ключ: $d=103$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【Фаза шифрования Алисы】&lt;/strong>
Предположим, мы хотим отправить сообщение $M = 9$.
Формула: $C \equiv 9^7 \pmod{143}$
$9^7 = 4,782,969$. Если разделить это число на 143, получим $33447$ с остатком $48$.
Зашифрованный текст получился $C = 48$.&lt;/p>
&lt;p>&lt;strong>【Фаза расшифрования Боба】&lt;/strong>
Боб получает зашифрованный текст $C = 48$ и использует закрытый ключ $d = 103$ для его расшифрования.
Формула: $M \equiv 48^{103} \pmod{143}$
Если запустить на калькуляторе или в программе &lt;code>(48 ** 103) % 143&lt;/code>, результат блестяще окажется равным &amp;ldquo;&lt;strong>9&lt;/strong>&amp;rdquo;! Исходное сообщение успешно получено.&lt;/p>
&lt;hr>
&lt;h2 id="8-как-найти-закрытый-ключ-d-расширенный-алгоритм-евклида">8. Как найти закрытый ключ $d$: Расширенный алгоритм Евклида
&lt;/h2>&lt;p>В примере с ручными вычислениями мы нашли $d=103$, подобрав $k$ интуитивно, но если числа состоят из сотен цифр, этот метод не сработает. В реальных программах используется алгоритм, называемый &lt;strong>&amp;ldquo;расширенным алгоритмом Евклида&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Решить $7d \equiv 1 \pmod{120}$ — это то же самое, что найти целые числа $d$ и $y$, удовлетворяющие уравнению $7d + 120y = 1$. Выполняя алгоритм Евклида в обратном порядке, это можно вычислить механически.&lt;/p>
&lt;ol>
&lt;li>$120 \div 7 = 17$, остаток $1$&lt;/li>
&lt;li>Если преобразовать это, получим $1 = 120 - 17 \times 7$&lt;/li>
&lt;li>То есть, $-17 \times 7 \equiv 1 \pmod{120}$&lt;/li>
&lt;/ol>
&lt;p>В мире по модулю $120$, число $-17$ имеет то же значение, что и $120 - 17 = 103$. Следовательно, мы мгновенно находим $d = 103$. Этот метод позволяет выполнять вычисления очень быстро даже для огромных чисел.&lt;/p>
&lt;hr>
&lt;h2 id="9-другое-лицо-rsa-цифровая-подпись">9. Другое лицо RSA: Цифровая подпись
&lt;/h2>&lt;p>Замечательной особенностью алгоритма RSA является то, что, поменяв ролями открытый и закрытый ключи, его можно использовать как &lt;strong>&amp;ldquo;цифровую подпись&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>При шифровании порядок был таким: &amp;ldquo;шифрование открытым ключом $\Rightarrow$ расшифрование закрытым ключом&amp;rdquo;,
а в цифровой подписи выполняется процедура &amp;ldquo;шифрование закрытым ключом $\Rightarrow$ расшифрование открытым ключом&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Алиса создает подпись с помощью закрытого ключа"] --> A2["S ≡ M^d (mod N)"]
A2 --> A3["Отправить сообщение M и подпись S"]
A3 --> B1["2. Боб проверяет подпись с помощью открытого ключа"]
B1 --> B2["Вычислить M' ≡ S^e (mod N)"]
B2 --> B3["Проверить, совпадают ли M' и M"]&lt;/div>
&lt;p>Алиса использует свой закрытый ключ $d$ для преобразования сообщения (это и есть подпись $S$) и отправляет его Бобу. Боб использует открытый ключ Алисы $e$ для выполнения проверочных вычислений. Если результат вычислений совпадает с исходным сообщением, это одновременно доказывает, что &amp;ldquo;эти данные могли быть созданы только с помощью закрытого ключа Алисы&amp;rdquo; и что &amp;ldquo;сообщение не было изменено в пути&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="10-rsa-на-практике-в-коде">10. RSA на практике в коде
&lt;/h2>&lt;p>Возведение в степень, которое сложно сделать вручную, очень легко реализовать с помощью Python. Ниже представлен код на Python, позволяющий опробовать основную логику алгоритма RSA.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Найти наибольший общий делитель&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Найти закрытый ключ d (используется встроенная функция Python 3.8 и выше)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Генерация ключей&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Открытый ключ: (N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">), Закрытый ключ: d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Шифрование&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Зашифрованный текст: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Расшифрование&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">decrypted_message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Расшифрованное сообщение: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Функция &lt;code>pow(base, exp, mod)&lt;/code> в Python внутри использует быстрый алгоритм, называемый &amp;ldquo;алгоритмом возведения в степень справа налево&amp;rdquo; (быстрое возведение в степень по модулю), поэтому вычисления для чисел из сотен цифр завершаются мгновенно.&lt;/p>
&lt;hr>
&lt;h2 id="11-заключение-и-будущее-криптографии">11. Заключение и будущее криптографии
&lt;/h2>&lt;p>Основываясь на знаниях школьной математики, мы разобрали механизмы работы алгоритма RSA.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Сложность факторизации:&lt;/strong> Легко вычислить $p \times q = N$, но очень сложно найти $p, q$ по заданному $N$.&lt;/li>
&lt;li>&lt;strong>Сравнения и теорема Эйлера:&lt;/strong> Благодаря закону $a^{\phi(N)} \equiv 1 \pmod N$ создается волшебная потайная дверь, позволяющая &amp;ldquo;вернуться к исходному значению при возведении в определенную степень&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Открытый и закрытый ключи:&lt;/strong> Зашифровать может кто угодно, но расшифровать способен только законный получатель.&lt;/li>
&lt;/ol>
&lt;p>Используемое в настоящее время в алгоритме RSA число $N$ состоит из более чем 600 цифр, и даже если задействовать все суперкомпьютеры в мире, разложение его на множители займет больше времени, чем возраст Вселенной. Однако, когда &amp;ldquo;квантовые компьютеры&amp;rdquo;, исследования которых ведутся в последние годы, будут введены в практическое использование в будущем, с помощью &amp;ldquo;алгоритма Шора&amp;rdquo; эту задачу факторизации можно будет решить за мгновение. По этой причине во всем мире сейчас ускоренными темпами ведется разработка &amp;ldquo;постквантовой криптографии&amp;rdquo;, которая не может быть взломана даже квантовыми компьютерами.&lt;/p>
&lt;p>Продвинутая математика, которая часто считается &amp;ldquo;бесполезной&amp;rdquo;, на самом деле фундаментально защищает нашу повседневную жизнь. Алгоритм RSA — это прекрасный учебный материал, который демонстрирует глубину и красоту математики. Мы надеемся, что благодаря этой статье вы смогли хотя бы немного почувствовать, насколько интересными могут быть криптография и математика.&lt;/p></description></item><item><title>Математические основы эллиптической криптографии (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></channel></rss>