实数域上的椭圆曲线与群
^^^^^^^^^^^^^^^^^^^^^^^^
原文: `Elliptic curves over real numbers and the group law `_
椭圆曲线(Elliptic Curves)
---------------------------
首先,什么是椭圆曲线,简单来说,椭圆曲线就是满足以下公式的点的集合:
.. math:: y^2 = x^3 + ax + b
其中 :math:`4a^3 + 27b^2 \ne 0` (排除掉奇异曲线 singular curves)。
.. figure:: images/curves.png
不同形状的椭圆曲线,:math:`b = 1`, :math:`a` 从 2 到 -3。
*a* 和 *b* 的值不一样,曲线在平面上的形状也不一样。显而易见并且容易证明的是:椭圆曲线都是相对于 x 轴对称的。
另外,我们定义无穷远点(point at infinity)为椭圆曲线上的一点,这个点我们用符号 :math:`0` 来表示。
加上无穷远点,完善后的椭圆曲线公式如下:
.. math:: \left\{ (x, y) \in \mathbb{R}^2\ |\ y^2 = x^3 + ax + b,\ 4 a^3 + 27 b^2 \ne 0 \right\}\ \cup\ \left\{ 0 \right\}
群(Groups)
----------------
在数学中,群是一个集合 :math:`\mathbb{G}` ,连同其上定义的二元运算 *加* (使用符号 + 表示)。要具备成为群的资格,这个集合和运算 :math:`(\mathbb{G}, +)` 还必须满足叫做群公理的四个要求:
1. 封闭性(closure):对于所有 :math:`\mathbb{G}` 中 a, b,运算 a + b 的结果也在 :math:`\mathbb{G}` 中。
2. 结合性(associativity):对于所有 :math:`\mathbb{G}` 中的 a, b 和 c,等式 (a + b) + c = a + (b + c) 成立。
3. 单位元(identity element):存在 :math:`\mathbb{G}` 中的一个元素 :math:`0`,使得对于所有 :math:`\mathbb{G}` 中的元素 a,等式 :math:`a + 0 = 0 + a = a` 成立。
4. 逆元(inverse):对于每个 :math:`\mathbb{G}` 中的 a,存在 :math:`\mathbb{G}` 中的一个元素 b 使得 :math:`a + b = 0`。
如果再加上第 5 个条件:
5. 交换性(commutativity): a + b = b + a 。
那么这个群又叫做阿贝尔群(abelian group)。
整数集合 :math:`\mathbb{Z}` 连同我们日常使用的整数加法构成一个群(还是一个阿贝尔群)。自然数集合 :math:`\mathbb{N}` 不是群,因为不满足第 4 个要求。
在以上特性成立的基础上,我们可以继续推导出群的一些其它特性,比如:单位元是唯一的,并且逆元也是唯一的,也就是说:对于任意 a,只存在唯一的 b 使得 a + b = 0(我们可以将 b 写做 -a)。这些特性在后文中会直接或间接的派上重要用场。
在椭圆曲线上定义一个群(The group law for elliptic curves)
-----------------------------------------------------------
我们可以如下定义一个椭圆曲线上的群:
- 群里的元素为曲线上的点。
- *单位元* 为无穷远点 :math:`0`。
- 曲线上任意一点 :math:`P` 的 *逆元* 是其相对于 x 轴的对称点。
- *加* 法规定如下:曲线上任意的 3 点 :math:`P`,:math:`Q`,:math:`R`,如果 3 点在一条直线上(aligned)并且都不是无穷远点(nonzero),那么它们的和 :math:`P + Q + R = 0` 。
.. image:: images/three-aligned-points.png
注意最后一条规则,我们只要求 3 个点在一条直线上,并不要求其顺序,也就是说 :math:`P + (Q + R) = Q + (P + R) = R + (P + Q) = \cdots = 0`,因此定义的加法满足结合性和交换性,也就是说这是一个阿贝尔群。
那么,我们如何计算任意两点相加的和呢?
几何加法(Geometric addition)
----------------------------------
上面定义的群是一个阿贝尔群,所以我们可以将 :math:`P + Q + R = 0` 改写成 :math:`P + Q = -R` 。从后面这个公式我们可以得出计算任意两点 :math:`P` 和 :math:`Q` 相加和的几何方法:过 :math:`P` 和 :math:`Q` 两点画一条直线,这条直线交曲线上第三点 :math:`R`,取其逆元 :math:`-R` 即是 :math:`P + Q` 的结果。
.. image:: images/point-addition.png
上面的几何计算方法可以工作但还需要几点补充,尤其是下面几个问题需要解决:
- **如果** :math:`P = 0` **或者** :math:`Q = 0` **怎么办?** 此时无法画一条过两点的直线,但是前面我们已经定义了 :math:`0` 为单位元,所以 :math:`P + 0 = P` ,:math:`0 + Q = Q` 。
- **如果** :math:`P = -Q` **呢?** 此时过两点的直线是垂直的,和曲线没有第三个交点。但是因为 :math:`P` 是 :math:`Q` 的逆元,根据逆元的定义: :math:`P + Q = P + (-P) = 0` 。
- **如果** :math:`P = Q` **呢?** 过一点有无数条直线,这里问题变得有点复杂了。考虑曲线上的一点 :math:`Q' \ne P` ,如果我们让 :math:`Q'` 不断逼近 :math:`P`,此时过 :math:`P` 和 :math:`Q'` 的直线就变成了曲线的切线。基于此,我们可以定义 :math:`P + P = -R` ,这里 :math:`R` 是曲线在 :math:`P` 点的切线与曲线的另外一个交点。
.. image:: images/animation-point-doubling.gif
- **如果** :math:`P \ne Q`,**但是没有第三个交点** :math:`R` **呢?** 这个和前面一个问题的情况类似,此时过 :math:`P` 和 :math:`Q` 的直线是曲线的切线。
.. image:: images/animation-tangent-line.gif
假设 :math:`P` 是切点,那么 :math:`P + P = -Q`,所以 :math:`P + Q = -P`,同理,如果 :math:`Q` 是切点,:math:`P + Q = -Q` 。
以上就是几何加法的完整步骤,使用笔和尺子我们就可以完成椭圆曲线上任意两点的加法(或者可以使用这个 `可视化工具`_ )。
代数加法(Algebraic addition)
----------------------------------
为了使用计算机来计算椭圆曲线上点的加法,我们需要将上面的几何方法转换为代数方法。将上面的规则转化为公式涉及到解三次方程,比较繁琐,所以这里我们省略过程直接给出结果。
首先,我们先去掉一些极限情况,我们知道 :math:`P + (-P) = 0`,也知道 :math:`P + 0 = 0 + P = P`,所以下面的公式中我们排除这两种情况,只考虑 :math:`P = (x_P, y_p)` 和 :math:`Q = (x_Q, y_Q)` 为非对称点、非无穷远点的情况。
因为 :math:`P` 和 :math:`Q` 非对称(:math:`x_P \ne x_Q`),所以过两点的直线有斜率(slope),斜率为:
.. math:: m = \frac{y_P - y_Q}{x_P - x_Q}
设直线与椭圆曲线的第三个交点为 :math:`R = (x_R, y_R)`,则:
.. math::
\begin{array}{rcl}
x_R & = & m^2 - x_P - x_Q \\
y_R & = & y_P + m(x_R - x_P)
\end{array}
或者:
.. math::
y_R = y_Q + m(x_R - x_Q)
我们使用一个例子来验证以下以上公式的正确性:根据我们的 `可视化工具`_ ,给定曲线 :math:`y^2 = x^3 - 7x + 10` ,:math:`P = (1, 2)` 和 :math:`Q = (3, 4)` ,两点的和 :math:`P + Q = -R = (-3, 2)` 。我们来看下和我们上面的公式计算的结果是否吻合:
.. math::
\begin{array}{rcl}
m & = & \frac{y_P - y_Q}{x_P - x_Q} = \frac{2 - 4}{1 - 3} = 1 \\
x_R & = & m^2 - x_P - x_Q = 1^2 - 1 - 3 = -3 \\
y_R & = & y_P + m(x_R - x_P) = 2 + 1 \cdot (-3 - 1) = -2 \\
& = & y_Q + m(x_R - x_Q) = 4 + 1 \cdot (-3 - 3) = -2
\end{array}
结果一致!
即使 :math:`P` **或者** :math:`Q` **中的一点是切点**,上面的公式依然可以得出正确的结果。例如: :math:`P = (-1, 4)` 和 :math:`Q = (1, 2)` 。
.. math::
\begin{array}{rcl}
m & = & \frac{y_P - y_Q}{x_P - x_Q} = \frac{4 - 2}{-1 - 1} = -1 \\
x_R & = & m^2 - x_P - x_Q = (-1)^2 - (-1) - 1 = 1 \\
y_R & = & y_P + m(x_R - x_P) = 4 + -1 \cdot (1 - (-1)) = 2
\end{array}
结果 :math:`P + Q = (1, -2)` ,和 `可视化工具`_ 给出的一样。
:math:`P = Q` **的情况需要特殊处理:** 计算 :math:`x_R` 和 :math:`y_R` 的公式不变,但是斜率的公式需要修改使用以下公式(因为 :math:`x_P = x_Q`):
.. math:: m = \frac{3x_P^2 + a}{2y_P}
此时,斜率 m 是下面这个公式的一阶导数:
.. math:: y_P = \pm \sqrt{x_P^3 + ax_P + b}
使用 :math:`P = Q = (1, 2)` 验证一下:
.. math::
\begin{array}{rcl}
m & = & \frac{3x_P^2 + a}{2 y_P} = \frac{3 \cdot 1^2 - 7}{2 \cdot 2} = -1 \\
x_R & = & m^2 - x_P - x_Q = (-1)^2 - 1 - 1 = -1 \\
y_R & = & y_P + m(x_R - x_P) = 2 + (-1) \cdot (-1 - 1) = 4
\end{array}
结果: :math:`P + P = -R = (-1,-4)` ,`正确 `_ !
.. _可视化工具: https://cdn.rawgit.com/andreacorbellini/ecc/920b29a/interactive/reals-add.html?px=-1&py=4&qx=1&qy=2
乘法(Scalar multiplication)
--------------------------------
除了加法之外,我们可以再定义一个运算:乘法。
.. math:: nP = \underbrace{P + P + \cdots + P}_{n\ \text{times}}
这里,:math:`n` 是一个自然数。 `乘法可视化计算工具 `_ 。
从乘法定义来看,计算 :math:`nP` 需要进行 :math:`n` 次加法运算。假如 :math:`n` 为 :math:`k` bit,则计算复杂度为: :math:`O(2^k)` ,性能不好,还好乘法存在不少快速算法。
**double and add** 就是其中算法之一。这个算法的原理可以用一个例子来解释清楚。令 :math:`n = 151` ,它的二进制表达形式为: :math:`10010111_2` ,这个二进制形式可以进一步用一系列 *2的幂(powers of two)* 的和来表示:
.. math::
\begin{array}{rcl}
151 & = & 1 \cdot 2^7 + 0 \cdot 2^6 + 0 \cdot 2^5 + 1 \cdot 2^4 + 0 \cdot 2^3 + 1 \cdot 2^2 + 1 \cdot 2^1 + 1 \cdot 2^0 \\
& = & 2^7 + 2^4 + 2^2 + 2^1 + 2^0
\end{array}
因此 :math:`151 \cdot P` 可以写成:
.. math:: 151 \cdot P = 2^7 P + 2^4 P + 2^2 P + 2^1 P + 2^0 P
最后,double and add 算法的计算步骤如下:
- 取 :math:`P` 。
- 计算(Double) :math:`2P = P + P` 。
- 将 :math:`2P` 和 :math:`P` 相加得到 :math:`2^1P + 2^0P` 的结果。
- 计算 :math:`2^2P = 2P + 2P` 。
- 将 :math:`2^2P` 和前面的结果相加得到 :math:`2^2P + 2^1P + 2^0P` 的结果。
- 计算 :math:`2^3P = 2^2P + 2^2P` 。
- 计算 :math:`2^4P = 2^3P + 2^3P` 。
- 将 :math:`2^4P` 和前面的结果相加得到 :math:`2^4P + 2^2P + 21^P + 2^0P` 的结果。
- ……
最终我们通过 7 次 Double 和 4 次加运算就得到了 :math:`151 \cdot P` 的结果。
如果上面的描述不够清晰,下面是该算法的 Python 代码实现:
.. code-block:: python
def bits(n):
"""
Generates the binary digits of n, starting
from the least significant bit.
bits(151) -> 1, 1, 1, 0, 1, 0, 0, 1
"""
while n:
yield n & 1
n >>= 1
def double_and_add(n, x):
"""
Returns the result of n * x, computed using
the double and add algorithm.
"""
result = 0
addend = x
for bit in bits(n):
if bit == 1:
result += addend
addend *= 2
return result
如果 Double 和加法的复杂度是 :math:`O(1)` ,那么本算法的复杂度就是 :math:`O(\log n)` (或者用 n 的 bit 长度表示的话: :math:`O(k)` ),性能很不错,比一开始 :math:`O(n)` 的复杂度好多了。
对数(Logarithm)
----------------------
给定 :math:`n` 和 :math:`P` ,我们有了一个算法可以在多项式时间内计算得到 :math:`Q = nP` 。那么反过来,如果我们知道 :math:`Q` 和 :math:`P` 需要计算出 :math:`n` 呢?这个问题被称作 **对数问题** ,称其为“对数”而不是“除”主要是为了和其它加密系统一致(这些系统里乘法对应的是幂 exponentiation)。
对数问题目前没有比较高效(easy)的解决算法,当然通过 `摸索 `_ 我们也能看到一些模式(pattern)。比如,曲线 :math:`y^2 = x^3 - 3x +1` 和点 :math:`P = (0, 1)` ,可以看到,当 :math:`n` 是奇数时,:math:`nP` 总是落在左边的曲线上,当 :math:`n` 是偶数时,:math:`nP` 落在右边的曲线上。通过不断的实验,我们也许可以发现更多的模式,这些模式可能最终可以帮我们找到一个解决对数问题的高效算法。
但是,对数问题中有一类 *离散* 对数问题,我们将在下文中看到,当我们缩小曲线的值域, **曲线上的乘法还是可以高效运算,但是其逆运算,也就是离散对数运算变得非常的困难(hard)**。这种不对称(duality)即椭圆曲线加密的核心。