<?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/zh-cn/tags/cryptography/</link><description>Recent content in Cryptography on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 21:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-cn/tags/cryptography/index.xml" rel="self" type="application/rss+xml"/><item><title>格密码（Lattice-based cryptography）的数学直觉</title><link>http://kenji.blog/zh-cn/p/lattice-based-cryptography-math-intuition/</link><pubDate>Fri, 11 Sep 2026 21:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/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）为首的公钥密码技术。这些密码方案的安全性建立在“整数分解问题”或“离散对数问题”等被认为使用传统经典计算机无法高效求解（需要指数级时间）的数学难题之上。&lt;/p>
&lt;p>然而，彼得·秀尔（Peter Shor）在1994年提出的“Shor算法”在密码学界引起了轩然大波。该算法在数学上证明了，一旦大规模量子计算机问世，便能在多项式时间内解决整数分解问题和离散对数问题。这意味着目前被广泛使用的公钥密码在未来将变得完全可以被破解。&lt;/p>
&lt;p>为了应对这种“量子计算机的威胁（Quantum Threat）”，亟需研究即使使用量子计算机也难以破解的新型密码方案。这就是被称为“后量子密码（Post-Quantum Cryptography: PQC）”或“抗量子计算密码”的领域。&lt;/p>
&lt;p>PQC有几种有力的候选方案。包括基于哈希的密码、基于编码的密码、多变量多项式密码、同源密码等，其中目前最受瞩目、也是NIST（美国国家标准与技术研究院）PQC标准化进程核心的便是“格密码（Lattice-based cryptography）”。与其他方案相比，格密码的加解密处理速度非常快，而且具有被称为“从最坏情况复杂度（Worst-case complexity）归约到平均情况复杂度（Average-case complexity）”的特性，在密码学理论中拥有极强的安全性证明。&lt;/p>
&lt;p>本文将从格密码的基础——“格（Lattice）”的数学定义出发，深入剖析格上的困难问题：SVP（最短向量问题）和CVP（最近向量问题），以及作为现代格密码心脏的“LWE问题（Learning With Errors）”，并结合数学公式、几何直觉以及具体的数值计算例子，进行透彻的讲解。&lt;/p>
&lt;h1 id="2-格lattice的数学定义与几何直觉">2. 格（Lattice）的数学定义与几何直觉
&lt;/h1>&lt;h2 id="21-向量空间与格">2.1 向量空间与格
&lt;/h2>&lt;p>在数学中，“格（Lattice）”是指在$n$维实向量空间 $\mathbb{R}^n$ 内规则排列的离散点的集合。它与线性代数中学习的向量空间（Vector Space）相似，但存在决定性的区别。向量空间是由基向量通过“实数系数”的线性组合表示的连续空间，而格则是通过基向量的“整数系数”的线性组合表示的离散空间。&lt;/p>
&lt;p>让我们给出严谨的数学定义。考虑在$m$维实向量空间 $\mathbb{R}^m$ 中的 $n$ 个（$n \le m$）线性无关向量 $\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n$。将这些向量作为列向量构成的矩阵记为 $B = [\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n] \in \mathbb{R}^{m \times n}$。这个 $B$ 被称为格的“基（Basis）”。&lt;/p>
&lt;p>由这个基 $B$ 生成的格 $\mathcal{L}(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}$。因此，它并不是空间中无限存在的连续点，而是形成了像等距排列的交叉点一样的“离散点集”。&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$ 的集合。这是最简单的“方格”。&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>有一个涉及格密码安全性根基的重要性质。那就是“生成同一个格的基有无数个”。&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$ 被称为“幺模矩阵（Unimodular matrix）”。&lt;/p>
&lt;p>在密码学应用中，基本思路是使用“好基（接近正交且由短向量组成的基）”作为私钥，而使用“坏基（相互极度倾斜且由非常长的向量组成的基）”作为公钥。当维度较高时，从坏基计算出好基将变得非常困难。这就是格密码的基本直觉。&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>用数学公式表示，即寻找使 $\min_{\mathbf{v} \in \mathcal{L}(B) \setminus \{\mathbf{0}\}} \| \mathbf{v} \|$ 成立的 $\mathbf{v}$。这个最小长度记作 $\lambda_1(\mathcal{L})$，被称为“格的第一连续极小值（First successive minimum）”。&lt;/p>
&lt;p>在二维或三维的低维度下，我们可以通过画图用肉眼找到最短向量。或者可以使用高斯格基约化算法等高效地求解。但是，当维度 $n$ 达到数百至数千这样的高维时，已知严格求解SVP是NP困难的。&lt;/p>
&lt;p>在实际密码中，通常不寻找严格的最短向量，而是寻找“近似短的向量”，即近似SVP（$\gamma$-SVP）。当近似系数 $\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{t}$ 最近的格点 $\mathbf{v} \in \mathcal{L}(B)$。&lt;/p>
&lt;p>用数学公式表示，即寻找使 $\min_{\mathbf{v} \in \mathcal{L}(B)} \| \mathbf{v} - \mathbf{t} \|$ 成立的格点 $\mathbf{v}$。&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算法（Lenstra-Lenstra-Lovász algorithm）。LLL算法能在多项式时间内运行，可以将格基约化（Reduction）到某种程度上的“好基”。然而，LLL算法所能找到的最短向量，其长度与真实最短向量相比，具有指数级（$2^{\mathcal{O}(n)}$）的近似系数，因此不足以破解密码的安全性。&lt;/p>
&lt;p>如果使用基于LLL改进的更强力的基约化算法（如BKZ，Block Korkine-Zolotarev算法），可以找到更短的向量，但其计算量会随着块大小呈指数级增长。在格密码中，就是通过估算这种BKZ算法的运行时间来决定安全参数（例如维度 $n$ 的大小）。在目前的PQC标准参数中，维度 $n$ 通常选择在500到1000以上的值，据称即使使用超级计算机或未来的量子计算机，破解所需的时间也将超过宇宙的年龄。&lt;/p>
&lt;h1 id="4-lwe问题learning-with-errors的数学公式化">4. LWE问题（Learning With Errors）的数学公式化
&lt;/h1>&lt;p>大部分现代格密码都基于Oded Regev在2005年提出的“LWE问题（Learning With Errors）”。LWE问题的美妙之处在于其公式化的简洁性，以及它拥有“从最坏情况复杂度到平均情况复杂度的归约”这一强大的数学证明。&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)$ 数对，我们就可以使用线性代数中的“高斯消元法（Gaussian elimination）”轻松恢复出秘密向量 $\mathbf{s}$。这是一个可以在多项式时间内轻松求解的问题。&lt;/p>
&lt;h2 id="42-lwe问题的定义加入噪声">4.2 LWE问题的定义：加入噪声
&lt;/h2>&lt;p>那么，如果在该问题中加入微小的“噪声（误差）”会怎样呢？
这就是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}$ 的问题就是“搜索LWE问题（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问题的一个变种——“判定LWE问题（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分布中获得的样本数对，与完全随机的数据对之间将变得“计算上不可区分（Computationally Indistinguishable）”。这个性质成为了基于LWE的密码能够生成“与随机数无法区分的密文”的理论依据。&lt;/p>
&lt;h2 id="44-从最坏情况复杂度到平均情况复杂度的归约regev定理">4.4 从最坏情况复杂度到平均情况复杂度的归约（Regev定理）
&lt;/h2>&lt;p>Oded Regev最大的贡献，就是通过数学证明将LWE问题的难度与前文所述格问题（SVP和CVP）的难度联系在了一起。&lt;/p>
&lt;p>他使用量子归约（Quantum reduction）证明了：“如果存在能在平均情况下（对于随机选择的 $A$ 和 $\mathbf{e}$）在多项式时间内求解LWE问题的算法，那么就存在能在多项式时间内求解任意格的最坏情况（最困难的情况）Gap-SVP的量子算法”。（后来Peikert等人也证明了经典归约）。&lt;/p>
&lt;p>这在密码学理论中是一个梦幻般的性质。因为它消除了“密码被破解，可能只是因为我们恰好选择了一个弱密钥（平均情况的一部分）”的担忧，并提供了强有力的保证：“如果平均的LWE能被解决，那么格上所有最困难的问题都能被解决（所以LWE绝对是困难的）”。&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构建公钥密码方案regev密码">5. 使用LWE构建公钥密码方案（Regev密码）
&lt;/h1>&lt;p>理解了LWE问题的困难性之后，我们来看看Oded Regev提出的基本公钥密码方案，了解如何利用它进行加解密。这里我们将讲解加密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>公钥本身正是“LWE问题的一个实例”。由于从公钥 $(A, \mathbf{b})$ 求出私钥 $\mathbf{s}$ 等同于求解搜索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>加密直观的含义是，对于公钥矩阵 $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{s}$ 对密文 $(\mathbf{u}, v)$ 进行解密。&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}$ 以内，鲍勃只需观察计算结果是更接近 $0$ 还是 $\lfloor \frac{q}{2} \rfloor$，就能准确地判定（恢复）消息 $M$。这就是基于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>仅仅罗列公式可能很难有切实的感受，所以让我们实际设定非常小的数值参数，来追踪一下从加密到解密的计算过程。
（※在现实的密码系统中，为了确保安全性，通常会使用 $n$ 为500以上、$q$ 为数千以上的值）&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$ 显然比 $0$ 更接近 $8$，所以鲍勃成功地正确恢复出了 $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）拥有极其强大的安全性证明，但在实际应用中却有一个致命的弱点。那就是“密钥尺寸巨大”和“计算成本高昂”。&lt;/p>
&lt;p>在Standard LWE中，公钥包含一个巨大的矩阵 $A \in \mathbb{Z}_q^{m \times n}$。当参数 $n$ 达到数百至数千时，这个矩阵的尺寸会达到几兆字节，每次在互联网通信协议（如TLS）中传输都会显得过于沉重。另外，矩阵与向量的乘法需要 $\mathcal{O}(n^2)$ 的计算量。&lt;/p>
&lt;p>为了解决这个问题，人们引入了将多项式环（Polynomial rings）这种代数结构融入格中的“Ring-LWE（RLWE）”和“Module-LWE（MLWE）”。&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$ 通常选择为2的幂）。&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>因为这是多项式的乘法，所以通过使用类似于快速傅里叶变换（FFT）的“数论变换（Number Theoretic Transform: NTT）”，可以将计算量急剧削减到 $\mathcal{O}(n \log n)$。此外，由于公钥的尺寸也从矩阵缩小到了单一的多项式，数据尺寸减少到了 $\mathcal{O}(n)$。这在通信带宽上带来了压倒性的优势。&lt;/p>
&lt;p>从数学上看，Ring-LWE并不是一般的格，而是归结为被称为“理想格（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虽然高效，但也存在着一些担忧，即理想格特殊的代数结构未来可能会成为被攻击的突破口。因此，结合了Standard LWE的保守安全性与Ring-LWE的高效性，做到了“两全其美”的，便是“Module-LWE（MLWE）”。&lt;/p>
&lt;p>在Module-LWE中，考虑的是以多项式为元素的小型矩阵和向量。也就是说，处理的是环上的模（Module）。
目前，NIST选定作为PQC密钥封装机制（KEM）标准的“CRYSTALS-Kyber”（标准化名称：ML-KEM），正是基于这个Module-LWE问题的困难性所构建的。&lt;/p>
&lt;h1 id="8-为什么对量子计算机也是安全的">8. 为什么对量子计算机也是安全的？
&lt;/h1>&lt;p>最后，我们来探讨一下核心问题：“为什么人们认为格密码即使使用量子计算机也无法被破解？”。&lt;/p>
&lt;p>量子计算机能够破解RSA密码和椭圆曲线密码的Shor算法，其本质是一个求解“隐藏子群问题（Hidden Subgroup Problem: HSP）”的算法。RSA和ECC背后的数学结构（有限阿贝尔群）具有周期性，通过使用被称为量子傅里叶变换（QFT）的量子算法特有操作，可以一口气提取出这个周期（隐藏的子群）。&lt;/p>
&lt;p>然而，格问题有着根本的不同。尽管格也具有周期性，但在SVP和CVP中需要求解的是“最短距离”或“去除噪声”这种几何上的非线性性质。即使直接套用像Shor算法中那样的“阿贝尔群上的量子傅里叶变换”，也无法高效地提取出成为格问题解答的有用信息。迄今为止，尚未发现能够在多项式时间内求解SVP或LWE的量子算法，人们普遍相信，即便拥有量子计算机的并行计算能力，也只有近乎穷举的搜索（如利用Grover算法带来的平方根加速程度）才是有效的手段。&lt;/p>
&lt;h1 id="9-总结">9. 总结
&lt;/h1>&lt;p>本文从格密码的数学直觉出发，详细讲解了从格的几何学定义，到LWE问题的公式化，再到构建公钥密码的全过程。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>格（Lattice）&lt;/strong> 是由基向量的整数系数线性组合表示的离散空间，在高维空间中，寻找接近正交的“好基”（SVP）是非常困难的。&lt;/li>
&lt;li>&lt;strong>LWE问题（Learning With Errors）&lt;/strong> 是求解带噪声的线性方程组的问题，由于它被归约到格的最坏情况问题的困难性，从而提供了强大的安全性依据。&lt;/li>
&lt;li>利用LWE问题，通过刻意添加或消除噪声的巧妙机制，实现了加解密（&lt;strong>Regev密码&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>在量子计算机这一前所未有的计算范式转变日益逼近的背景下，脱胎于经典线性代数和数论深渊的“格密码”，将承担起未来互联网安全基石的重任，这着实是一个充满浪漫色彩的故事。作为格密码基础的数学绝非高不可攀，只要具备线性代数和概率的基础知识，就足以理解其美妙的结构。希望本文能为您理解作为PQC核心的格密码提供一些帮助。&lt;/p></description></item><item><title>黎曼猜想与素数分布：与现代密码学的深刻联系</title><link>http://kenji.blog/zh-cn/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/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>在探索素数之谜的道路上，最接近真相的是1859年由德国数学家波恩哈德·黎曼（Bernhard Riemann）提出的**“黎曼猜想（Riemann Hypothesis）”**。黎曼猜想是现代数学中最重要且未解决的难题之一，也是克雷数学研究所设立的千禧年大奖难题之一，悬赏100万美元。&lt;/p>
&lt;p>乍看之下，关于素数分布的纯数学难题似乎与我们的日常生活毫不相干。然而，支撑现代社会基础设施的互联网安全，尤其是&lt;strong>RSA加密和椭圆曲线密码学（ECC）等现代密码技术&lt;/strong>，都深深依赖于巨大素数的性质。&lt;/p>
&lt;p>本文将带领读者进行一次数学之旅，从素数分布、素数定理、黎曼zeta函数，一直深入到黎曼猜想的核心，并极其详尽地探讨它如何与现代密码技术产生联系，以及如果黎曼猜想被证明，世界将会发生怎样的改变。&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岁的天才数学家卡尔·弗里德里希·高斯（Carl Friedrich Gauss）计算了庞大的素数表，并发现素数出现的频率与自然对数 $\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-黎曼zeta函数与欧拉乘积">3. 黎曼zeta函数与欧拉乘积
&lt;/h1>&lt;p>分析素数分布最强大的武器是&lt;strong>黎曼zeta函数（Riemann Zeta Function）&lt;/strong>。它最初是由莱昂哈德·欧拉（Leonhard Euler）针对实数 $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> 研究zeta函数，就等同于研究素数的分布。&lt;/p>
&lt;hr>
&lt;h1 id="4-解析延拓与向复平面的扩展">4. 解析延拓与向复平面的扩展
&lt;/h1>&lt;p>黎曼的天才之处在于，他将欧拉原本只在实数范围内考虑的 $\zeta(s)$ 的变量 $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>他进一步推导出了zeta函数满足的优美函数方程（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>使zeta函数值为0的复数 $s$ 被称为“零点”。
从函数方程可知，当 $s$ 是负偶数（$-2, -4, -6, \dots$）时，由于 $\sin(\pi s / 2)$ 变为0，因此 $\zeta(s) = 0$。这些被称为&lt;strong>平凡零点（Trivial zeros）&lt;/strong>。&lt;/p>
&lt;p>然而，在素数分布中真正重要的是其他零点，也就是存在于 $0 \le \sigma \le 1$ 的“临界带（Critical strip）”中的&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函数 $\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["黎曼zeta函数 ζ(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>为什么黎曼猜想如此重要？那是因为zeta函数的零点&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$ 是对zeta函数的所有非平凡零点求和。
主项是 $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$ 会利用公钥 $e, N$ 转换为密文 $C$。
&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加密，就必须从巨大的 $N$ 中找出原来的素数 $p$ 和 $q$（进行质因数分解）。即使使用当前主流的算法（如普通数域筛选法：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>目前在实际应用中使用的是一种被称为**米勒-拉宾素性测试（Miller-Rabin primality test）**的概率算法。这种算法速度很快，但存在以极低概率将合数误判为素数的“伪素数”风险。&lt;/p>
&lt;p>然而，如果假设将黎曼猜想扩展到狄利克雷L函数上的**“广义黎曼猜想（Generalized Riemann Hypothesis, GRH）”&lt;strong>为真，情况将发生戏剧性的变化。
如果GRH为真，那么在数学上就能保证米勒-拉宾测试的测试次数上限，使其从概率算法&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>为了严格评估光滑数出现的频率，需要对素数分布有深刻的理解，这里也充分利用了直接联系zeta函数和黎曼猜想的解析数论技巧。如果黎曼猜想被证明，并且素数分布的误差被完全确定，那么将有可能更准确地看清质因数分解算法的性能极限。&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>如果确立了某种能够完全操控黎曼zeta函数零点性质的未知代数几何学方法或非交换几何学方法，那么这可能会导致发现具有突破性的质因数分解算法（例如将计算复杂度降低到多项式时间的经典算法），这种可能性是无法否认的。正因如此，密码学家们绝对无法将视线从黎曼猜想的动向上移开。&lt;/p>
&lt;h3 id="量子计算机与shor算法">量子计算机与Shor算法
&lt;/h3>&lt;p>对于密码技术而言，更直接、更现实的威胁不是黎曼猜想的证明，而是&lt;strong>量子计算机&lt;/strong>。彼得·秀尔（Peter Shor）在1994年发表的“Shor算法”证明，如果拥有性能足够强大的量子计算机，就能在多项式时间内解决质因数分解问题。这将导致RSA加密和椭圆曲线加密被从根本上破解。&lt;/p>
&lt;p>目前，全世界都在推进向即使是量子计算机也无法破解的“抗量子密码（Post-Quantum Cryptography, PQC）”（如格密码等）的过渡。依赖于素数的密码技术在某种意义上可能正在迎来其黄金时代的落幕，但素数本身的数学价值将永远不会消失。&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>自古希腊以来对素数不懈的探索，通过黎曼这位天才，升华为了复平面上优美的交响乐（zeta函数的零点）。令人惊叹的是，那纯粹无暇的数学结晶，历经数个世纪，如今已被应用为保障互联网社会安全的最强盾牌。&lt;/p>
&lt;p>黎曼猜想是一个同时象征着数学所具有的“抽象之美”与“对物理世界、现实社会惊人的适用能力”的存在。&lt;/p>
&lt;p>当这座尚未有人登顶的巍峨数学高山在未来的某一天被征服时，我们不仅将完全理解素数这一宇宙真理，而且会对信息化社会的基础产生全新的视角。学习密码技术，本身也是一趟追溯人类智慧历史的旅程。&lt;/p></description></item><item><title>密码技术的历史：从凯撒密码到抗量子密码（PQC）</title><link>http://kenji.blog/zh-cn/p/history-of-cryptography-caesar-to-pqc/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/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）是保持信息机密性的技术，伴随着人类历史不断演进。从古代战争中秘密指令的传递，到现代互联网中信用卡信息的保护，密码的目的始终如一，即“让预期的接收者能够理解信息，而让第三方无法解读”。&lt;/p>
&lt;p>在现代信息安全中，密码技术不仅仅局限于“信息的隐藏（机密性：Confidentiality）”，还承担着保障数据“完整性（Integrity）”、“认证（Authentication）”和“不可否认性（Non-repudiation）”等重要作用。&lt;/p>
&lt;p>本文将从古代简单的替换密码开始，涵盖机械密码、现代对称密钥与公钥密码，直到因量子计算机实用化而到来的“抗量子密码（PQC）”时代，从技术和数学的角度，为您详细梳理密码技术演进的历史。&lt;/p>
&lt;hr>
&lt;h1 id="2-古典密码时代字符的替换与重排">2. 古典密码时代：字符的替换与重排
&lt;/h1>&lt;p>密码的起源可以追溯到公元前。早期的密码主要由“置换（重排）”和“替换（代换）”两种方法构成。&lt;/p>
&lt;h2 id="斯库塔里密码置换密码">斯库塔里密码（置换密码）
&lt;/h2>&lt;p>公元前5世纪，古希腊斯巴达使用的“斯库塔里（Scytale）”是最古老的密码工具之一。它将一条细长的羊皮纸缠绕在特定粗细的木棒上，然后横向写下信息。解下羊皮纸后，字符的排列顺序会变得毫无意义，但拥有相同粗细木棒的接收者重新缠上羊皮纸，就能读出原始信息。&lt;/p>
&lt;h2 id="凯撒密码单表替换密码">凯撒密码（单表替换密码）
&lt;/h2>&lt;p>公元前1世纪，相传古罗马英雄朱利叶斯·凯撒使用的是“凯撒密码”。这是一种将字母表平移特定位数（通常为3个字符）的单表替换密码（Monoalphabetic substitution）。&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>单表替换密码很快就被9世纪阿拉伯学者阿尔·金迪提出的“频率分析（Frequency Analysis）”轻易破解。这是利用了语言的统计特征，例如在英语中“E”和“T”出现频率极高。&lt;/p>
&lt;p>为了对抗这种破解方法，16世纪诞生了“维吉尼亚密码（Vigenère cipher）”。这是一种周期性切换使用多个偏移量（密钥）的多表替换密码（Polyalphabetic substitution），在长达约300年的时间里被称为“不可破译的密码（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世纪，查尔斯·巴贝奇和弗里德里希·卡西斯基发现了“卡西斯基测试（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世纪，通信手段从信件演变为电报和无线电，对加密的速度和复杂性提出了更高要求。此时，由多个转子（旋转盘）组合而成的“机械密码”应运而生。&lt;/p>
&lt;h2 id="恩尼格玛enigma的威胁">恩尼格玛（Enigma）的威胁
&lt;/h2>&lt;p>二战期间，纳粹德国使用的“恩尼格玛（Enigma）”是密码史上最著名的密码机。恩尼格玛由多个转子（通常为3到4个）、用于互换字符接线的接线板（Steckerbrett）以及反射器（反转转子）组成。&lt;/p>
&lt;p>每次在键盘上敲击一个字符，转子都会旋转，因此即使连续输入相同的字符，也会输出不同的密码字符（多表替换密码的极致）。其密钥空间（设置组合）高达约 $1.58 \times 10^{19}$ 种，以当时的技术水平，暴力破解被认为是不可能的。&lt;/p>
&lt;h2 id="艾伦图灵与炸弹机bombe">艾伦·图灵与“炸弹机（Bombe）”
&lt;/h2>&lt;p>向这台坚不可摧的恩尼格玛发起挑战的，是继承了波兰数学家马里安·雷耶夫斯基等人早期成果的英国布莱切利园密码破译团队。&lt;/p>
&lt;p>特别是艾伦·图灵（Alan Turing），他利用对应于部分密文的明文推测（Crib），开发出了机电式破译机“炸弹机（Bombe）”。炸弹机能够高速检测逻辑矛盾，接连排除不可能的转子设置，最终成功破解了恩尼格玛。据称，这一伟业使盟军的胜利提前了数年。&lt;/p>
&lt;hr>
&lt;h1 id="4-现代密码的开端对称密钥密码des与aes">4. 现代密码的开端：对称密钥密码（DES与AES）
&lt;/h1>&lt;p>战后，随着计算机的出现，密码实现了从“字符”操作到“比特（0和1）”操作的剧烈范式转变。&lt;/p>
&lt;h2 id="克劳德香农与信息论">克劳德·香农与信息论
&lt;/h2>&lt;p>1949年，克劳德·香农发表了论文《保密系统的通信理论》，奠定了现代密码学的数学基础。他提出了安全密码设计的两大原则：“混淆（Confusion）”与“扩散（Diffusion）”。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>混淆（Confusion）&lt;/strong>: 尽可能使密钥与密文之间的关系复杂化。（通过替换或S盒实现）&lt;/li>
&lt;li>&lt;strong>扩散（Diffusion）&lt;/strong>: 使明文的1比特变化能够影响密文中的多个比特。（通过置换或重排实现）&lt;/li>
&lt;/ul>
&lt;h2 id="des-数据加密标准">DES (数据加密标准)
&lt;/h2>&lt;p>1977年，美国国家标准技术研究所（NIST，当时称为NBS）将基于IBM设计的“DES”制定为标准密码。
DES采用了被称为“费斯妥网络（Feistel Network）”的架构，具有64比特的分组长度和56比特的密钥长度。其在实现上的一大优势是，加密和解密算法的结构几乎完全相同。&lt;/p>
&lt;p>然而，随着计算机计算能力的提升，56比特的密钥长度（约 $7.2 \times 10^{16}$ 种组合）已明显不足。1998年，电子前哨基金会（EFF）开发了专用机器“Deep Crack”，仅用数天时间便成功破解了DES。&lt;/p>
&lt;h2 id="aes-高级加密标准">AES (高级加密标准)
&lt;/h2>&lt;p>作为替代DES的新标准，2001年“AES”应运而生。它采用了通过公开征集选拔出的，由比利时密码学家设计的“Rijndael”算法。&lt;/p>
&lt;p>AES没有采用费斯妥网络，而是采用了“SPN结构（Substitution-Permutation Network）”，并利用了伽罗瓦域（有限域） $GF(2^8)$ 上的数学运算。其密钥长度可选择128、192或256比特，至今仍在全球范围内被广泛用作标准的对称密钥密码。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "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-公钥密码的革命从diffie-hellman到rsa">5. 公钥密码的革命：从Diffie-Hellman到RSA
&lt;/h1>&lt;p>对称密钥密码存在一个致命的弱点，那就是“密钥分配问题（Key Distribution Problem）”。即在开始加密通信之前，如何与远方的通信对象安全地共享“对称密钥”。解决这一问题的，正是诞生于20世纪70年代的“公钥密码”。&lt;/p>
&lt;h2 id="diffie-hellman密钥交换">Diffie-Hellman密钥交换
&lt;/h2>&lt;p>1976年，惠特菲尔德·迪菲和马丁·赫尔曼发表了具有划时代意义的论文《密码学的新方向》。他们利用“离散对数问题（Discrete Logarithm Problem）”的数学难题，提出了一种即使在通信链路被窃听的情况下也能安全共享密钥的方法。&lt;/p>
&lt;ol>
&lt;li>公开一个大素数 $p$ 和生成元 $g$。&lt;/li>
&lt;li>Alice选择一个秘密值 $a$，计算 $A = g^a \pmod{p}$ 并发送给Bob。&lt;/li>
&lt;li>Bob选择一个秘密值 $b$，计算 $B = g^b \pmod{p}$ 并发送给Alice。&lt;/li>
&lt;li>Alice计算 $K = B^a \pmod{p}$，Bob计算 $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年，罗纳德·李维斯特、阿迪·萨莫尔和伦纳德·阿德曼三人共同发明了“RSA密码”。它是基于“对巨大的合数进行素数分解极其困难”这一性质。&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>选择一个与 $\phi(n)$ 互素的整数 $e$（公钥）。&lt;/li>
&lt;li>计算满足 $e \times d \equiv 1 \pmod{\phi(n)}$ 的整数 $d$（私钥）。&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年有人提出了“椭圆曲线密码（Elliptic Curve Cryptography: ECC）”。它利用了有限域上椭圆曲线（通常形式为 $y^2 = x^3 + ax + b$）上点的加法运算。&lt;/p>
&lt;p>众所周知，椭圆曲线上的离散对数问题（ECDLP）比素数分解问题更难求解。&lt;strong>利用ECC，仅需256比特的密钥长度就能实现与3072比特RSA相当的安全性&lt;/strong>。这也使得在智能手机或物联网（IoT）设备等计算资源有限的环境下，仍能实现高速且安全的加密通信（如ECDSA或ECDH等）。&lt;/p>
&lt;hr>
&lt;h1 id="7-量子计算机的威胁与抗量子密码pqc">7. 量子计算机的威胁与抗量子密码（PQC）
&lt;/h1>&lt;p>密码技术曾经显得坚不可摧，但1994年彼得·秀尔（Peter Shor）发表的“秀尔算法”却带来了巨大冲击。&lt;/p>
&lt;p>量子计算机利用量子力学中“叠加态”与“量子纠缠”的特性进行计算。数学上已经证明，若在性能足够强大的量子计算机上运行秀尔算法，就能在“多项式时间”内解决素数分解问题和离散对数问题。这意味着，在实用的量子计算机问世之日（Q-Day），目前广泛使用的RSA、ECC等公钥密码将会在瞬间被攻破。&lt;/p>
&lt;h2 id="pqc后量子密码抗量子密码的登场">PQC（后量子密码/抗量子密码）的登场
&lt;/h2>&lt;p>为了应对这一前所未有的威胁，基于量子计算机也难以破解的新数学难题的“抗量子密码（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）所采用。其基于在多维空间的“格（Lattice）”中寻找特定点的问题（如最短向量问题：SVP）以及LWE（Learning With Errors：容错学习）问题的困难性。&lt;/p>
&lt;p>LWE问题的概念是，在联立一次方程组中故意加入“微小的噪声（误差）”，利用这会立刻导致难以求解的性质。
方程组: $\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"># 即使使用量子计算机，从 A 和 b 还原 s 也被认为极其困难&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年提出的McEliece密码就颇为著名，其历史悠久且安全性备受肯定，但也存在公钥体积过大（甚至可达数兆字节）的问题。&lt;/p>
&lt;div class="mermaid">timeline
title "密码技术与计算机演进历史"
"古代至中世纪" : "凯撒密码" : "维吉尼亚密码" : "频率分析的诞生"
"1930至40年代" : "恩尼格玛的应用与破解" : "图灵机与炸弹机的开发"
"1970年代" : "DES标准化 (1977)" : "Diffie-Hellman密钥交换 (1976)" : "RSA密码诞生 (1977)"
"1980至90年代" : "椭圆曲线密码 (ECC) 的提出" : "秀尔算法发表 (1994)"
"2000年代" : "AES标准化 (2001)"
"2010年代至今" : "量子计算机研究加速" : "NIST启动PQC标准化项目"
"近未来 (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等强力密码，也面临着量子计算机这一全新“矛”的威胁。&lt;/p>
&lt;p>然而，人类已经着眼于更遥远的未来，正在准备抗量子密码（PQC）这一面崭新的“盾”。目前，对于全球的IT基础设施而言，从现有的公钥密码向PQC过渡的准备工作（确保密码敏捷性，Crypto Agility）已成为当务之急。&lt;/p>
&lt;p>密码技术不仅仅是深奥晦涩的数学难题，它更是保护我们的隐私、财产乃至整个社会基础设施的最强防线。&lt;/p></description></item><item><title>【数学解说】让高中生也能看懂的RSA加密原理</title><link>http://kenji.blog/zh-cn/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/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加密”。在线购物的信用卡支付、与朋友的SNS交流、公司机密信息的收发等，我们每天都在不经意间使用的很多通信，都是由RSA加密或其后继技术保护着的。&lt;/p>
&lt;p>然而，一听到“密码（加密）”，你可能会想象出间谍电影中出现的复杂密码机，或者只有少数天才才能理解的超高级数学。确实，现代密码学基于高级数学，但&lt;strong>RSA加密的根本原理，只要有高中学过的数学（整数的性质、素数、同余式等）知识就足以理解&lt;/strong>。&lt;/p>
&lt;p>本文将以高中数学知识为起点，逐步彻底解说RSA加密是基于怎样的数学原理运行的，以及为什么破解它如此困难。为了让稍微不擅长数学的人也能理解，我们将结合具体例子进行详细说明。&lt;/p>
&lt;hr>
&lt;h2 id="1-对称加密与非对称加密公开密钥加密">1. 对称加密与非对称加密（公开密钥加密）
&lt;/h2>&lt;p>在进入RSA加密的数学原理之前，我们先来整理一下加密的基本概念。加密方式大致分为“对称加密（共享密钥加密）”和“非对称加密（公开密钥加密）”两种。&lt;/p>
&lt;h3 id="11-对称加密方式的局限性">1.1 对称加密方式的局限性
&lt;/h3>&lt;p>自古以来使用的密码大多被称为“对称加密方式”。这是一种&lt;strong>在“加密（将消息转换为秘密的密文）”和“解密（将密文还原为原始消息）”时使用相同密钥&lt;/strong>的方式。&lt;/p>
&lt;p>例如，爱丽丝想给鲍勃发送一封秘密信件。爱丽丝用挂锁（对称密钥）把信锁在盒子里。鲍勃要打开那个盒子，就必须拥有与爱丽丝使用的相同的钥匙。&lt;/p>
&lt;p>这种方式存在一个大问题，那就是“密钥分发问题”。相隔遥远的爱丽丝和鲍勃在第一次通信时，该如何共享密钥而不被窃听呢？如果在邮寄密钥的途中被第三方窃取，之后的加密通信将全部泄露。&lt;/p>
&lt;h3 id="12-划时代的发明公开密钥加密方式">1.2 划时代的发明“公开密钥加密方式”
&lt;/h3>&lt;p>为了解决这个密钥分发问题而发明的就是“公开密钥加密方式”。RSA加密也是其中一种。&lt;/p>
&lt;p>在公开密钥加密方式中，使用**“用于加密的密钥（公钥）”和“用于解密的密钥（私钥）”这两个不同的密钥**。&lt;/p>
&lt;ol>
&lt;li>接收者鲍勃创建一对“公钥”和“私钥”。&lt;/li>
&lt;li>鲍勃将“公钥”向全世界公开（任何人获取都可以）。&lt;/li>
&lt;li>发送者爱丽丝使用鲍勃的“公钥”对消息进行加密并发送。&lt;/li>
&lt;li>加密后的消息，只有拥有鲍勃“私钥”的人（即鲍勃自己）才能解密。&lt;/li>
&lt;/ol>
&lt;p>如果用挂锁来比喻，鲍勃制作了许多“处于打开状态的挂锁（公钥）”并散布到全世界。爱丽丝把要寄给鲍勃的信放进盒子里，捡起鲍勃的挂锁“咔哒”一声锁上。挂锁一旦锁上，就只能用鲍勃拥有的“万能钥匙（私钥）”才能打开。即使有人中途偷了盒子，因为没有万能钥匙也无法打开。&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;/p>
&lt;hr>
&lt;h2 id="2-支撑rsa加密的数学基础1素数与素因数分解">2. 支撑RSA加密的数学基础1：素数与素因数分解
&lt;/h2>&lt;p>RSA加密的安全性基于**“对巨大数字进行素因数分解非常困难”**这一数学事实。&lt;/p>
&lt;h3 id="21-什么是素数">2.1 什么是素数
&lt;/h3>&lt;p>素数是指“只能被1和自身整除的、大于1的自然数”。
例如：$2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>素数就像是所有整数的“原子”。任何自然数都可以分解成素数相乘的形式。这被称为&lt;strong>素因数分解&lt;/strong>。例如，$60 = 2^2 \times 3 \times 5$，如果不考虑顺序，素因数分解的方式只有一种，这就是著名的“算术基本定理”。&lt;/p>
&lt;h3 id="22-素因数分解的困难性单向函数">2.2 素因数分解的困难性（单向函数）
&lt;/h3>&lt;p>这里重要的是**“乘法很容易，但素因数分解很难”**这种非对称性。&lt;/p>
&lt;p>例如，请心算下面两个素数的乘法：
$11 \times 13 = ?$
这很简单吧。答案是 $143$。&lt;/p>
&lt;p>那么，下面这个数呢？
请对 $323$ 进行素因数分解。
怎么样？应该会花点时间吧。（答案是 $17 \times 19$）。&lt;/p>
&lt;p>如果数字很小，人类还能勉强计算出来，但当数字变大时，即使使用计算机，计算也会呈爆炸式增长。目前主流的RSA加密中，使用了2048位（十进制约600位）这样极其巨大的素数 $p$ 和 $q$ 相乘得到的数 $N = p \times q$。&lt;/p>
&lt;p>当给出两个巨大的素数 $p$ 和 $q$ 时，计算机计算 $N$ 只需要一瞬间（不到一毫秒）。然而，反过来如果只给出 $N$，要找出原来的 $p$ 和 $q$，即使让目前最快的超级计算机运行数万亿年也解不出来。&lt;/p>
&lt;p>这种**“计算的非对称性（去向容易，逆向困难）”**，构成了公钥和私钥之间关系的基础。&lt;/p>
&lt;hr>
&lt;h2 id="3-支撑rsa加密的数学基础2同余式模运算">3. 支撑RSA加密的数学基础2：同余式（模运算）
&lt;/h2>&lt;p>RSA加密的计算，并不是我们在日常生活中使用的无限变大的加法或乘法，而是在除以某个数后的“余数”世界里进行的。这被称为&lt;strong>同余式（模运算）&lt;/strong>。&lt;/p>
&lt;h3 id="31-钟表数学">3.1 钟表数学
&lt;/h3>&lt;p>模运算常被比喻为“钟表数学”。假设现在是10点，那么5小时后是几点？虽然 $10 + 5 = 15$ 点，但在普通的12小时制钟表上，我们会回答“3点”。这是因为15除以12的余数是3。&lt;/p>
&lt;p>在数学世界中，这被写成如下形式：
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
读作“15和3在模12下同余（除以12的余数相等）”。&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>特别重要的是“乘方”的性质。这意味着**“余数的乘方，等于乘方的余数”**。
例如，假设我们想求 $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加密核心的魔法数学。作为“费马小定理”的推广，“欧拉定理”即将登场。&lt;/p>
&lt;h3 id="41-欧拉函数-phin">4.1 欧拉函数 $\phi(N)$
&lt;/h3>&lt;p>欧拉函数（$\phi$ 函数）是指，对于某个自然数 $N$，**“返回从1到 $N$ 的自然数中，与 $N$ 互质（最大公约数为1）的数的个数”**的函数。&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>
这个性质起到了RSA加密中“秘密后门（陷门）”的作用。知道 $p$ 和 $q$ 的人（密钥创建者）可以瞬间计算出 $\phi(N)$，而只知道 $N$ 的第三方，除非对 $N$ 进行素因数分解，否则无法求出 $\phi(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>这是一个惊人的性质，意思是“将某个数 $a$ 连续相乘 $\phi(N)$ 次后除以 $N$，余数必定是 $1$”。（当 $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;/p>
&lt;hr>
&lt;h2 id="5-rsa加密的算法密钥生成加密解密的步骤">5. RSA加密的算法：密钥生成、加密、解密的步骤
&lt;/h2>&lt;p>基础知识准备齐全后，终于可以来看RSA加密的具体步骤了。RSA加密大致分为“1. 密钥生成”、“2. 加密”和“3. 解密”三个阶段。&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["选择与 φ(N) 互质的 e"]
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>接收者鲍勃将生成属于自己的“公钥”和“私钥”。&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> 选择一个满足 $1 &lt; e &lt; \phi(N)$，且与 $\phi(N)$ 互质的整数 $e$。&lt;/li>
&lt;li>&lt;strong>计算私钥 $d$:&lt;/strong> 找到一个满足以下条件的整数 $d$。
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
换句话说，就是寻找“一个数 $d$，使得 $e \times d$ 除以 $\phi(N)$ 的余数是 $1$”。&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>计算“消息 $M$ 的 $e$ 次方，除以 $N$ 的余数 $C$”。这个 $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>计算“密文 $C$ 的 $d$ 次方，除以 $N$ 的余数”，结果竟然奇迹般地恢复成了原始消息 $M$！&lt;/p>
&lt;hr>
&lt;h2 id="6-为什么解密能恢复原状数学证明">6. 为什么解密能恢复原状？（数学证明）
&lt;/h2>&lt;p>你可能会感到疑惑：“为什么只要对 $C$ 求 $d$ 次方，就能恢复成原来的 $M$ 呢？”这时，刚才提到的“欧拉定理”就要大显身手了。&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)}$ 来选择的。这意味着“$ed$ 是 $\phi(N)$ 的倍数加上 $1$”。使用整数 $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$，所以只有拥有 $d$ 的鲍勃，才能从 $C$ 中提取出 $M$。&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>选择与 $\phi(N)=120$ 互质的公钥 $e$。这里我们选 $e=7$。&lt;/li>
&lt;li>求解私钥 $d$。寻找满足 $7 \times d \equiv 1 \pmod{120}$ 的 $d$。
在方程 $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>，结果精彩地显示为“&lt;strong>9&lt;/strong>”！我们成功收到了原来的消息。&lt;/p>
&lt;hr>
&lt;h2 id="8-求私钥-d-的方法扩展欧几里得算法">8. 求私钥 $d$ 的方法：扩展欧几里得算法
&lt;/h2>&lt;p>在手工计算的例子中，我们是靠直觉寻找 $k$ 来得出 $d=103$ 的，但当数字达到几百位时，这种方法就行不通了。实际的程序中会使用名为**“扩展欧几里得算法”**的算法。&lt;/p>
&lt;p>求解 $7d \equiv 1 \pmod{120}$，就等同于寻找满足 $7d + 120y = 1$ 的整数 $d$ 和 $y$。通过逆向推导欧几里得算法，就可以机械地求出这个解。&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>$-17$ 在模 $120$ 的世界里，与 $120 - 17 = 103$ 意义相同。因此，瞬间就能求出 $d = 103$。这种方法无论数字多大，都能非常快速地进行计算。&lt;/p>
&lt;hr>
&lt;h2 id="9-rsa加密的另一面数字签名">9. RSA加密的另一面：数字签名
&lt;/h2>&lt;p>RSA加密非常棒的一点是，通过反转公钥和私钥的作用，它还可以用作**“数字签名”**。&lt;/p>
&lt;p>在加密时，是“用公钥加密 $\Rightarrow$ 用私钥解密”，
而在数字签名中，步骤变成了“用私钥加密 $\Rightarrow$ 用公钥解密”。&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$ 进行验证计算。如果计算结果与原始消息一致，就能同时证明“这是只有爱丽丝的私钥才能生成的数据”以及“消息在传输过程中没有被篡改”。&lt;/p>
&lt;hr>
&lt;h2 id="10-通过程序感受rsa加密">10. 通过程序感受RSA加密
&lt;/h2>&lt;p>手工计算非常困难的乘方计算，如果使用Python就能非常轻松地实现。以下是可以体验RSA加密核心逻辑的Python代码。&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>Python 的 &lt;code>pow(base, exp, mod)&lt;/code> 函数在内部使用了称为“快速幂（反复平方法）”的高效算法，所以即使是几百位的数字，也能在一瞬间完成计算。&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$ 很容易计算，但从 $N$ 找出 $p, q$ 却极其困难。&lt;/li>
&lt;li>&lt;strong>同余式与欧拉定理:&lt;/strong> 根据 $a^{\phi(N)} \equiv 1 \pmod N$ 这一法则，完成了“乘方某次后恢复原状”的魔法陷门。&lt;/li>
&lt;li>&lt;strong>公钥与私钥:&lt;/strong> 任何人都可以加密，但只有合法的接收者才能解密。&lt;/li>
&lt;/ol>
&lt;p>目前使用的RSA加密中，$N$ 超过600位，即使动员全世界的超级计算机，进行素因数分解也需要花费超过宇宙年龄的时间。然而，随着近年来“量子计算机”研究的推进，如果在未来投入实用，通过“秀尔算法”，这种素因数分解可能会被瞬间破解。因此，目前世界各地正在加紧开发连量子计算机都无法破解的“抗量子计算密码（后量子密码学）”。&lt;/p>
&lt;p>那些常被认为“没有用处”的高级数学，实际上正在底层默默保护着我们的日常生活。RSA加密正是向我们展示这种数学深度与美感的最佳教材。希望通过这篇文章，能让您哪怕多感受到一点密码学与数学的乐趣。&lt;/p></description></item><item><title>椭圆曲线密码学（ECC）的数学基础与C++实现</title><link>http://kenji.blog/zh-cn/p/elliptic-curve-cryptography-math-cpp/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/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>在现代密码技术中，**椭圆曲线密码学（Elliptic Curve Cryptography: ECC）**发挥着极其重要的作用。从我们日常的互联网通信（HTTPS/TLS）、智能手机的安全隔离区（Secure Enclave）、通过SSH进行的服务器身份验证、FIDO等无密码认证，甚至到比特币和以太坊等加密资产，可以说现代数字社会的信任基础正是由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）的安全基础是另一种数学困难性，即**“椭圆曲线上的离散对数问题（ECDLP）”**。直到现在，尚未发现能够有效解决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">椭圆曲线密码的密钥长度（位）&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位。这使得减少计算量、降低内存使用量和节约网络带宽成为可能，特别是在资源有限的物联网设备和智能卡环境中，ECC拥有压倒性的优势。&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>**群（Group）**是指由一个集合 $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>在密码学理论中，我们使用的不是像实数或复数那样具有连续且无限元素的域，而是元素数量有限的**有限体（Finite Field）**或称伽罗瓦域（Galois Field）。&lt;/p>
&lt;p>最基本的有限体是使用素数 $p$ 构成的&lt;strong>素体 $\mathbb{F}_p$&lt;/strong>。它是在整数集合 $\{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}$ 是模 $p$ 下 $b$ 的乘法逆元）&lt;/li>
&lt;/ul>
&lt;p>**乘法逆元（Modular Multiplicative Inverse）**的计算在密码学实现中极为重要。为了求出满足 $b \times b^{-1} \equiv 1 \pmod p$ 的 $b^{-1}$，主要使用以下两种算法：&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>**椭圆曲线（Elliptic Curve）&lt;strong>通常由以下被称为&lt;/strong>魏尔斯特拉斯标准型（Weierstrass normal form）**的方程所定义的平面曲线：&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>为了使椭圆曲线在数学上构成一个完备的群，除了平面上的点之外，我们引入了一个称为**“无穷远点（Point at Infinity）”**的虚拟点。记作 $\mathcal{O}$（字母 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 = (x, y)$ 的逆元 $-P$，定义为关于 x 轴对称的点 $(x, -y)$。因此：
&lt;/p>
$$ P + (-P) = \mathcal{O} $$
&lt;p>
成立。&lt;/p>
&lt;hr>
&lt;h2 id="4-椭圆曲线上的群运算点的加法与二倍算">4. 椭圆曲线上的群运算（点的加法与二倍算）
&lt;/h2>&lt;p>椭圆曲线密码学的核心在于曲线上点与点之间的**“加法（Addition）”**运算。这不同于普通整数的加法，它是基于几何操作来定义的。&lt;/p>
&lt;h3 id="41-几何加法切线与割线法--tangent-and-chord-method">4.1. 几何加法（切线与割线法 / Tangent and Chord Method）
&lt;/h3>&lt;p>将曲线上不同的两点 $P$ 和 $Q$ 相加，求得新点 $R$ ($R = 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 + P = 2P$），我们无法画出穿过两点的直线。在这种情况下，我们要画出&lt;strong>曲线在点 $P$ 处的切线（Tangent）&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>我们需要将几何操作转化为代数公式，以便计算机可以进行计算。
所有的运算都在**有限体 $\mathbb{F}_p$ 上（模 $p$）**进行。&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$（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]
这些公式包含了形如 $(x_2 - x_1)^{-1}$ 或 $(2y_1)^{-1}$ 的&lt;strong>除法（模逆元的计算）&lt;/strong>。模逆元的计算成本非常高，因此在实际实现中，通常会使用诸如**“雅可比坐标系（Jacobian Coordinates）”**等射影坐标系来延迟除法运算。&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$，求满足 $Q = kP$ 的标量 $k$。&lt;/p>
&lt;/blockquote>
&lt;p>利用后文所述的算法，由 $k$ 和 $P$ 计算 $Q$（正向）是很简单的（多项式时间），但由 $P$ 和 $Q$ 逆推 $k$（反向）除了暴力穷举外没有有效的解法，实际上是不可能的（单向函数）。
在密码协议中，&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>，则在 $R$ 上加上 $P$（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>在此，我们将讲解 ECC 最典型的应用案例——&lt;strong>ECDH（Elliptic Curve Diffie-Hellman）密钥交换协议&lt;/strong>的机制。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 的困难性，无法从 $Q_A = d_A \cdot G$ 推导得出爱丽丝的私钥 $d_A$。此外，即使将 $Q_A$ 和 $Q_B$ 相乘也不会得到共享密钥 $S$，因此窃听者无法计算出 $S$。&lt;/p>
&lt;hr>
&lt;h2 id="7-实现中的陷阱侧信道攻击及其对策">7. 实现中的陷阱：侧信道攻击及其对策
&lt;/h2>&lt;p>即使在理论上是完美的密码算法，在将其实现为程序的过程中，也有可能产生漏洞。这就是**“侧信道攻击（Side-Channel Attack）”**。&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>这个实现存在一个致命的缺陷。由于只有当位为 &lt;code>1&lt;/code> 时才会执行 Point Addition，所以当位为 &lt;code>1&lt;/code> 时所需的&lt;strong>计算时间会比位为 &lt;code>0&lt;/code> 时稍微长一些&lt;/strong>。此外，处理器的分支预测和缓存内存的行为也会发生变化。
攻击者通过对这微小的计算时间（或功耗）差异进行数千次的统计观测，就可以&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>为了防止计时攻击，我们需要采用一种无论**私钥的位值如何，执行的指令序列和计算时间始终保持不变（Constant-Time）**的算法。&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>Montgomery Ladder 的优美之处在于，无论位是 &lt;code>0&lt;/code> 还是 &lt;code>1&lt;/code>，都会**“始终执行一次 Point Addition 和一次 Point Doubling”**。这样就完全排除了计算时间对数据的依赖性。&lt;/p>
&lt;p>然而，如果代码中存在分支（&lt;code>if (k_i == 0)&lt;/code>）本身，那么由于编译器的优化或CPU的分支预测，执行时间仍然有波动的风险。因此，在实际的恒定时间实现中，我们会消除条件分支（&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">// 以 secp256k1 的素数 p 和参数为例
&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> 这样动态分配的多精度整数类并不适合 Constant-Time 处理。因为内存分配和数组大小的变化会泄露计时信息。在实用库中，使用固定长度（例如 uint64_t × 4 个元素的数组）来表示，并实现位级别的掩码处理。以下是概念性示例。&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">// 概念性的 Constant-Time Swap (假设使用定长整数)
&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，则掩码全为 1 (0xFF..)；为 0，则全为 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">// ※ 尽管使用 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-使用-montgomery-ladder-的标量乘法">8.4. 使用 Montgomery Ladder 的标量乘法
&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 (Montgomery Ladder 方式)
&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 的位长 (若是 secp256k1 则为 256 位)
&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">// 总是执行相同的运算 (Point Addition 和 Point Doubling)
&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>: 能在非常短的密钥长度下提供强大的安全性，是现代移动互联网和物联网时代的最佳选择。&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>: 使用 Montgomery Ladder 和 Conditional Swap 来均衡硬件级别的行为，从而防范攻击的 C++ 编码技术。&lt;/li>
&lt;/ol>
&lt;p>实际上，因为极高的安全风险，业界不建议（&amp;ldquo;Don&amp;rsquo;t roll your own crypto&amp;rdquo;）自行编写用于生产环境的密码学库。然而，深入理解其内部运行的算法和数学背景，对于设计和运维更安全、高性能系统的工程师来说，必定是一件不可或缺的强大武器。&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>